-- name: CreateBatchUpload :one
INSERT INTO batch_uploads (client_id, original_filename, total_documents)
VALUES ($1, $2, $3) RETURNING id;

-- name: CreateBatchUploadWithStorage :one
INSERT INTO batch_uploads (
    client_id, 
    original_filename, 
    total_documents, 
    status,
    archive_bucket, 
    archive_key,
    file_size_bytes
) VALUES ($1, $2, $3, $4::batch_status, $5, $6, $7) 
RETURNING id, created_at;

-- name: GetBatchUpload :one
SELECT id, client_id, original_filename, total_documents, processed_documents, 
       failed_documents, invalid_type_documents, status, progress_percent,
       failed_filenames, created_at, completed_at
FROM batch_uploads 
WHERE id = $1 AND client_id = $2;

-- name: GetBatchUploadWithStorage :one
SELECT id, client_id, original_filename, total_documents, 
       processed_documents, failed_documents, invalid_type_documents,
       status, archive_bucket, archive_key, file_size_bytes,
       failed_filenames, created_at, completed_at
FROM batch_uploads 
WHERE id = $1 AND client_id = $2;

-- name: ListBatchUploads :many
SELECT id, client_id, original_filename, total_documents, processed_documents, 
       failed_documents, invalid_type_documents, status, progress_percent,
       created_at, completed_at
FROM batch_uploads 
WHERE client_id = $1
ORDER BY created_at DESC
LIMIT $2 OFFSET $3;

-- name: SetBatchTotalDocuments :exec
UPDATE batch_uploads SET total_documents = $2 WHERE id = $1;

-- name: UpdateBatchProgress :exec
UPDATE batch_uploads
SET processed_documents = $2,
    failed_documents = $3,
    invalid_type_documents = $4,
    progress_percent = $5
WHERE id = $1;

-- name: UpdateBatchStatus :exec
UPDATE batch_uploads 
SET status = $2::batch_status,
    completed_at = CASE WHEN $2::batch_status IN ('completed', 'failed', 'cancelled') THEN NOW() ELSE NULL END
WHERE id = $1;

-- name: SetBatchCompletedAt :exec
UPDATE batch_uploads
SET completed_at = @completed_at
WHERE id = @id;

-- name: AddFailedFilename :exec
UPDATE batch_uploads 
SET failed_filenames = failed_filenames || jsonb_build_array($2::text)
WHERE id = $1;

-- name: GetDocumentsByBatchId :many
SELECT id, clientId, hash 
FROM documents 
WHERE batch_id = $1;

-- name: CountDocumentsByBatchId :one
SELECT COUNT(*) as count
FROM documents 
WHERE batch_id = $1;

-- name: GetUnprocessedBatches :many
SELECT id, client_id, original_filename, total_documents,
       processed_documents, failed_documents, invalid_type_documents,
       status, progress_percent, created_at, completed_at
FROM batch_uploads
WHERE status = 'processing'
ORDER BY created_at ASC;

-- name: InsertBatchDocumentOutcome :exec
-- Upsert: if a row for this (batch_id, filename) already exists (e.g. batch
-- retry after partial failure), overwrite it with the latest outcome.
INSERT INTO batch_document_outcomes (batch_id, filename, outcome, error_detail, document_id)
VALUES ($1, $2, $3::batch_outcome_status, $4, $5)
ON CONFLICT (batch_id, filename) DO UPDATE
SET outcome = EXCLUDED.outcome,
    error_detail = EXCLUDED.error_detail,
    document_id = EXCLUDED.document_id,
    updated_at = NOW();

-- name: UpdateBatchDocumentOutcomeByDocumentID :many
-- Updates the outcome for a document progressing through the pipeline.
-- Only updates rows in non-terminal pipeline states to prevent overwriting
-- terminal outcomes (e.g. a 'duplicate' row from a different batch that
-- shares the same document_id).
UPDATE batch_document_outcomes
SET outcome = $2::batch_outcome_status,
    error_detail = $3,
    updated_at = NOW()
WHERE document_id = $1
  AND outcome IN ('submitted', 'init_complete', 'sync_complete')
RETURNING batch_id;

-- name: ResolveBatchDocumentOutcome :exec
-- Called by docInitRunner to set the document_id on a previously-submitted
-- outcome row and update the outcome to an init-stage result.
-- Finds the row by batch_id + filename since document_id is NULL at this point.
UPDATE batch_document_outcomes
SET document_id = $3,
    outcome = $4::batch_outcome_status,
    error_detail = $5,
    updated_at = NOW()
WHERE batch_id = $1 AND filename = $2 AND document_id IS NULL;

-- name: ListBatchDocumentOutcomes :many
-- Returns all outcome rows for a batch, with clean failure detail
-- joined from currentCleanEntries when applicable.
SELECT
    bdo.id,
    bdo.batch_id,
    bdo.filename,
    bdo.outcome,
    bdo.error_detail,
    bdo.document_id,
    bdo.created_at,
    bdo.updated_at,
    cce.fail as clean_fail
FROM batch_document_outcomes bdo
LEFT JOIN currentCleanEntries cce ON cce.documentId = bdo.document_id
WHERE bdo.batch_id = $1
ORDER BY bdo.created_at ASC;

-- name: RecordBatchFolderCandidate :exec
-- Records a non-root folder touched by a batch upload. Once a batch observes a
-- folder as newly created, retries must preserve that fact.
INSERT INTO batch_folder_candidates (batch_id, folder_id, path, existed_before)
VALUES (@batch_id, @folder_id, @path, @existed_before)
ON CONFLICT (batch_id, folder_id) DO UPDATE
SET path = EXCLUDED.path,
    existed_before = batch_folder_candidates.existed_before AND EXCLUDED.existed_before;

-- name: ListBatchFolderCandidates :many
SELECT batch_id, folder_id, path, existed_before, created_at
FROM batch_folder_candidates
WHERE batch_id = @batch_id
ORDER BY path;

-- name: LockBatchUploadForFolderCleanup :one
SELECT id, status, total_documents, created_at, completed_at, folder_cleanup_completed_at
FROM batch_uploads
WHERE id = @batch_id
FOR UPDATE;

-- name: CountBatchOutcomes :one
SELECT COUNT(*)::int
FROM batch_document_outcomes
WHERE batch_id = @batch_id;

-- name: CountNonTerminalBatchOutcomes :one
SELECT COUNT(*)::int
FROM batch_document_outcomes
WHERE batch_id = @batch_id
  AND outcome IN ('submitted', 'init_complete', 'sync_complete');

-- name: CountAcceptedBatchOutcomes :one
SELECT COUNT(*)::int
FROM batch_document_outcomes
WHERE batch_id = @batch_id
  AND outcome IN ('clean_passed', 'sync_skipped');

-- name: ListFolderCleanupReadyBatchIDs :many
-- @sqlc-vet-disable
SELECT bu.id
FROM batch_uploads bu
WHERE bu.folder_cleanup_completed_at IS NULL
  AND bu.status IN ('completed', 'failed', 'cancelled')
  AND EXISTS (
      SELECT 1
      FROM batch_folder_candidates bfc
      WHERE bfc.batch_id = bu.id
  )
  AND (
      (
          bu.total_documents > 0
          AND
          (SELECT COUNT(*) FROM batch_document_outcomes bdo WHERE bdo.batch_id = bu.id) = bu.total_documents
          AND NOT EXISTS (
              SELECT 1
              FROM batch_document_outcomes bdo
              WHERE bdo.batch_id = bu.id
                AND bdo.outcome IN ('submitted', 'init_complete', 'sync_complete')
          )
      )
      OR (
          bu.status IN ('failed', 'cancelled')
          AND (SELECT COUNT(*) FROM batch_document_outcomes bdo WHERE bdo.batch_id = bu.id) > 0
          AND bu.completed_at IS NOT NULL
          AND bu.completed_at <= NOW() - (sqlc.arg(stale_after_seconds)::int * INTERVAL '1 second')
      )
  )
ORDER BY bu.created_at ASC
LIMIT sqlc.arg(limit_count);

-- name: DetachRejectedBatchDocumentsFromCandidateFolders :execrows
-- @sqlc-vet-disable
UPDATE documents d
SET folderId = NULL
WHERE d.batch_id = @batch_id
  AND d.folderId IS NOT NULL
  AND EXISTS (
      SELECT 1
      FROM batch_folder_candidates bfc
      WHERE bfc.batch_id = @batch_id
        AND bfc.folder_id = d.folderId
  )
  AND EXISTS (
      SELECT 1
      FROM batch_document_outcomes bdo
      WHERE bdo.batch_id = @batch_id
        AND bdo.document_id = d.id
        AND (
            bdo.outcome IN (
                'duplicate',
                'init_duplicate',
                'invalid_type',
                'failed_open',
                'failed_read',
                'failed_s3_upload',
                'failed_upload',
                'clean_failed'
            )
            OR (@include_nonterminal::boolean AND bdo.outcome IN ('submitted', 'init_complete', 'sync_complete'))
        )
  );

-- name: DetachRejectedBatchDocumentUploadsFromCandidateFolders :execrows
-- @sqlc-vet-disable
UPDATE documentUploads du
SET folder_id = NULL
WHERE du.batch_id = @batch_id
  AND du.folder_id IS NOT NULL
  AND EXISTS (
      SELECT 1
      FROM batch_folder_candidates bfc
      WHERE bfc.batch_id = @batch_id
        AND bfc.folder_id = du.folder_id
  )
  AND EXISTS (
      SELECT 1
      FROM batch_document_outcomes bdo
      WHERE bdo.batch_id = @batch_id
        AND bdo.filename = du.filename
        AND (
            bdo.outcome IN (
                'duplicate',
                'init_duplicate',
                'invalid_type',
                'failed_open',
                'failed_read',
                'failed_s3_upload',
                'failed_upload',
                'clean_failed'
            )
            OR (@include_nonterminal::boolean AND bdo.outcome IN ('submitted', 'init_complete', 'sync_complete'))
        )
  );

-- name: MarkBatchFolderCleanupCompleted :exec
UPDATE batch_uploads
SET folder_cleanup_completed_at = NOW()
WHERE id = @batch_id
  AND folder_cleanup_completed_at IS NULL;
