import { buildSourceFilePath } from '@/lib/sourceFileUtils';
import { getSupabaseClient, sourceFilesBucket } from '@/lib/supabase';
import { SourceFileExtractionStatus, SourceFileRecord } from '@/types';

interface UploadSourceFileInput {
  batchId: string;
  file: File;
  uploadedBy?: string;
}

export interface SourceFileExtractionStatePatch {
  extraction_status: SourceFileExtractionStatus;
  extraction_model?: string | null;
  extraction_summary?: string | null;
  extraction_error?: string | null;
  extracted_material_count?: number | null;
}

function shouldStampExtractedAt(status: SourceFileExtractionStatus): boolean {
  return status === 'local_parsed' || status === 'llm_succeeded' || status === 'llm_failed';
}

export async function listSourceFiles(batchId?: string): Promise<SourceFileRecord[]> {
  const supabase = getSupabaseClient();

  let query = supabase
    .from('source_files')
    .select('*')
    .order('uploaded_at', { ascending: false });

  if (batchId) {
    query = query.eq('batch_id', batchId);
  }

  const { data, error } = await query;
  if (error) throw new Error(error.message);

  return (data ?? []) as SourceFileRecord[];
}

export async function batchIdExists(batchId: string): Promise<boolean> {
  const supabase = getSupabaseClient();
  const { data, error } = await supabase
    .from('source_files')
    .select('id')
    .eq('batch_id', batchId)
    .limit(1);

  if (error) throw new Error(error.message);

  return (data?.length ?? 0) > 0;
}

export async function uploadSourceFile({ batchId, file, uploadedBy }: UploadSourceFileInput): Promise<SourceFileRecord> {
  const supabase = getSupabaseClient();
  const storagePath = buildSourceFilePath(batchId, file.name);

  const { error: uploadError } = await supabase.storage
    .from(sourceFilesBucket)
    .upload(storagePath, file, {
      cacheControl: '3600',
      contentType: file.type || undefined,
      upsert: false,
    });

  if (uploadError) {
    throw uploadError;
  }

  const { data, error: insertError } = await supabase
    .from('source_files')
    .insert({
      batch_id: batchId,
      file_name: file.name,
      storage_path: storagePath,
      mime_type: file.type || null,
      size_bytes: file.size,
      uploaded_by: uploadedBy?.trim() || null,
      extraction_status: 'uploaded',
    })
    .select('*')
    .single();

  if (insertError) {
    await supabase.storage.from(sourceFilesBucket).remove([storagePath]);
    throw new Error(insertError.message);
  }

  return data as SourceFileRecord;
}

export async function updateSourceFileExtractionState(
  recordId: string,
  patch: SourceFileExtractionStatePatch,
): Promise<SourceFileRecord> {
  const supabase = getSupabaseClient();
  const updates: SourceFileExtractionStatePatch & { extracted_at?: string | null } = {
    ...patch,
  };

  if (shouldStampExtractedAt(patch.extraction_status)) {
    updates.extracted_at = new Date().toISOString();
  } else if (patch.extraction_status === 'llm_processing') {
    updates.extracted_at = null;
  }

  const { data, error } = await supabase
    .from('source_files')
    .update(updates)
    .eq('id', recordId)
    .select('*')
    .maybeSingle();

  if (error) throw new Error(error.message);
  if (!data) {
    throw new Error('源文件识别状态更新失败：记录不存在或当前账号没有更新权限。');
  }

  return data as SourceFileRecord;
}

export async function deleteSourceFileById(recordId: string): Promise<SourceFileRecord | null> {
  const supabase = getSupabaseClient();

  const { data: existingRecord, error: existingError } = await supabase
    .from('source_files')
    .select('*')
    .eq('id', recordId)
    .maybeSingle();

  if (existingError) throw new Error(existingError.message);
  if (!existingRecord) return null;

  const { data, error } = await supabase
    .from('source_files')
    .delete()
    .eq('id', recordId)
    .select('*')
    .maybeSingle();

  if (error) throw new Error(error.message);
  if (!data) {
    throw new Error('源文件删除失败：记录存在，但当前账号没有删除权限。');
  }

  const deletedRecord = data as SourceFileRecord;
  // Best-effort storage cleanup — don't fail the whole operation if only the file removal fails
  try {
    await supabase.storage
      .from(sourceFilesBucket)
      .remove([deletedRecord.storage_path]);
  } catch {
    // Storage file may already be gone or bucket may not exist; DB record is already deleted
  }

  return deletedRecord;
}

export async function createSourceFileDownloadUrl(storagePath: string) {
  const supabase = getSupabaseClient();
  const { data, error } = await supabase.storage
    .from(sourceFilesBucket)
    .createSignedUrl(storagePath, 60 * 10);

  if (error) throw new Error(error.message);

  return data.signedUrl;
}
