import { Job } from "@/src/entities/models/job";
import { JobAcquisitionError } from "@/src/entities/errors/job-errors";
import { NotFoundError } from "@/src/entities/errors/common";
import { z } from "zod";
import { PaginatedList } from "@/src/entities/common/paginated-list";

/**
 * Schema for creating a new job.
 * Defines the required fields when creating a job in the system.
 */
export const CreateJobSchema = Job.pick({
    reason: true,
    projectId: true,
    input: true,
});

export const ListedJobItem = Job.pick({
    id: true,
    projectId: true,
    status: true,
    reason: true,
    createdAt: true,
    updatedAt: true,
});

/**
 * Schema for filtering jobs when listing.
 * This schema is designed to be extensible for future filtering criteria.
 */
export const JobFiltersSchema = z.object({
    // Filter by job status
    status: z.enum(["pending", "running", "completed", "failed"]).optional(),
    
    // Filter by recurring job rule ID
    recurringJobRuleId: z.string().optional(),
    
    // Filter by composio trigger deployment ID
    composioTriggerDeploymentId: z.string().optional(),
    
    // Filter by date range
    createdAfter: z.string().datetime().optional(),
    createdBefore: z.string().datetime().optional(),
    
    // Extensible: add more filters here as needed
    // Example: errorMessage: z.string().optional(),
    // Example: priority: z.enum(["low", "medium", "high"]).optional(),
}).strict();

/**
 * Schema for updating an existing job.
 * Defines the fields that can be updated for a job.
 */
export const UpdateJobSchema = Job.pick({
    status: true,
    output: true,
});

/**
 * Repository interface for managing jobs in the system.
 * 
 * This interface defines the contract for job management operations including
 * creation, polling, locking, updating, and releasing jobs. Jobs represent
 * asynchronous tasks that can be processed by workers.
 */
export interface IJobsRepository {
    /**
     * Creates a new job in the system.
     * 
     * @param data - The job data containing trigger information, project ID, and input
     * @returns Promise resolving to the created job with all fields populated
     */
    create(data: z.infer<typeof CreateJobSchema>): Promise<z.infer<typeof Job>>;

    /**
     * Fetches a job by its unique identifier.
     * 
     * @param id - The unique identifier of the job to fetch
     * @returns Promise resolving to the job or null if not found
     */
    fetch(id: string): Promise<z.infer<typeof Job> | null>;

    /**
     * Polls for the next available job that can be processed by a worker.
     * 
     * This method should return the next job that is in "pending" status and
     * is not currently locked by another worker.
     * 
     * @param workerId - The unique identifier of the worker requesting a job
     * @returns Promise resolving to the next available job or null if no jobs are available
     */
    poll(workerId: string): Promise<z.infer<typeof Job> | null>;

    /**
     * Locks a specific job for processing by a worker.
     * 
     * This method should mark the job as "running" and associate it with the
     * specified worker ID to prevent other workers from processing it.
     * 
     * @param id - The unique identifier of the job to lock
     * @param workerId - The unique identifier of the worker locking the job
     * @returns Promise resolving to the locked job
     * @throws {JobAcquisitionError} if the job is already locked or doesn't exist
     */
    lock(id: string, workerId: string): Promise<z.infer<typeof Job>>;

    /**
     * Updates an existing job with new status and/or output data.
     * 
     * @param id - The unique identifier of the job to update
     * @param data - The data to update (status and/or output)
     * @returns Promise resolving to the updated job
     * @throws {NotFoundError} if the job doesn't exist
     */
    update(id: string, data: z.infer<typeof UpdateJobSchema>): Promise<z.infer<typeof Job>>;

    /**
     * Releases a job lock, making it available for other workers.
     * 
     * This method should clear the workerId association and potentially
     * reset the status back to "pending" if the job was not completed.
     * 
     * @param id - The unique identifier of the job to release
     * @returns Promise that resolves when the job has been released
     */
    release(id: string): Promise<void>;

    /**
     * Lists jobs for a specific project with optional filtering and pagination.
     * 
     * @param projectId - The unique identifier of the project
     * @param filters - Optional filters to apply to the job list
     * @param cursor - Optional cursor for pagination
     * @param limit - Maximum number of jobs to return (default: 50)
     * @returns Promise resolving to a paginated list of jobs
     */
    list(
        projectId: string, 
        filters?: z.infer<typeof JobFiltersSchema>,
        cursor?: string, 
        limit?: number
    ): Promise<z.infer<ReturnType<typeof PaginatedList<typeof ListedJobItem>>>>;

    /**
     * Deletes all jobs associated with a specific project.
     * 
     * @param projectId - The unique identifier of the project
     * @returns Promise resolving to void
     */
    deleteByProjectId(projectId: string): Promise<void>;
}