import { eq, and, count } from 'drizzle-orm'
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'
import { files } from '../db/schema/files.js'
import type { StorageProvider } from '../storage/index.js'
import { randomUUID } from 'crypto'
import { ValidationError } from '../lib/errors.js'

const ALLOWED_IMAGE_TYPES = ['image/jpeg', 'image/png', 'image/webp']
const ALLOWED_PDF_TYPES = ['application/pdf']
const ALLOWED_TYPES = [...ALLOWED_IMAGE_TYPES, ...ALLOWED_PDF_TYPES]
const MAX_IMAGE_SIZE = 10 * 1024 * 1024 // 10 MB
const MAX_PDF_SIZE = 25 * 1024 * 1024 // 25 MB
const MAX_FILES_PER_ENTITY = 20

function getExtension(contentType: string): string {
  const map: Record<string, string> = {
    'image/jpeg': 'jpg',
    'image/png': 'png',
    'image/webp': 'webp',
    'application/pdf': 'pdf',
  }
  return map[contentType] ?? 'bin'
}

export const FileService = {
  async upload(
    db: PostgresJsDatabase<any>,
    storage: StorageProvider,
    input: {
      data: Buffer
      filename: string
      contentType: string
      entityType: string
      entityId: string
      category: string
      uploadedBy?: string
    },
  ) {
    // Validate content type
    if (!ALLOWED_TYPES.includes(input.contentType)) {
      throw new ValidationError(`File type not allowed: ${input.contentType}`)
    }

    // Validate size
    const maxSize = ALLOWED_IMAGE_TYPES.includes(input.contentType) ? MAX_IMAGE_SIZE : MAX_PDF_SIZE
    if (input.data.length > maxSize) {
      throw new ValidationError(`File too large: ${input.data.length} bytes (max ${maxSize})`)
    }

    // Check per-entity limit
    const [existing] = await db
      .select({ total: count() })
      .from(files)
      .where(
        and(
          eq(files.entityType, input.entityType),
          eq(files.entityId, input.entityId),
        ),
      )
    if (existing.total >= MAX_FILES_PER_ENTITY) {
      throw new ValidationError(`Maximum ${MAX_FILES_PER_ENTITY} files per entity`)
    }

    // Generate path
    const fileId = randomUUID()
    const ext = getExtension(input.contentType)
    const path = `${input.entityType}/${input.entityId}/${input.category}-${fileId}.${ext}`

    // Write to storage
    await storage.put(path, input.data, input.contentType)

    // Insert record
    const [file] = await db
      .insert(files)
      .values({
        id: fileId,
        path,
        filename: input.filename,
        contentType: input.contentType,
        sizeBytes: input.data.length,
        entityType: input.entityType,
        entityId: input.entityId,
        category: input.category,
        uploadedBy: input.uploadedBy,
      })
      .returning()

    return file
  },

  async getById(db: PostgresJsDatabase<any>, id: string) {
    const [file] = await db
      .select()
      .from(files)
      .where(eq(files.id, id))
      .limit(1)
    return file ?? null
  },

  async listByEntity(
    db: PostgresJsDatabase<any>,
    entityType: string,
    entityId: string,
  ) {
    return db
      .select()
      .from(files)
      .where(
        and(
          eq(files.entityType, entityType),
          eq(files.entityId, entityId),
        ),
      )
      .orderBy(files.createdAt)
  },

  async delete(
    db: PostgresJsDatabase<any>,
    storage: StorageProvider,
    id: string,
  ) {
    const file = await this.getById(db, id)
    if (!file) return null

    await storage.delete(file.path)

    const [deleted] = await db
      .delete(files)
      .where(eq(files.id, id))
      .returning()

    return deleted ?? null
  },
}
