import { eq, and, count, inArray, isNull, isNotNull, gte, lte, type Column, type SQL } from 'drizzle-orm'
import type { PostgresJsDatabase } from 'drizzle-orm/postgres-js'
import {
  repairTickets,
  repairLineItems,
  repairBatches,
  repairServiceTemplates,
  repairNotes,
} from '../db/schema/repairs.js'
import type {
  RepairTicketCreateInput,
  RepairTicketUpdateInput,
  RepairLineItemCreateInput,
  RepairLineItemUpdateInput,
  RepairBatchCreateInput,
  RepairBatchUpdateInput,
  RepairNoteCreateInput,
  RepairServiceTemplateCreateInput,
  RepairServiceTemplateUpdateInput,
  PaginationInput,
} from '@lunarfront/shared/schemas'
import {
  withPagination,
  withSort,
  buildSearchCondition,
  paginatedResponse,
} from '../utils/pagination.js'

async function generateUniqueNumber(
  db: PostgresJsDatabase<any>,
  table: typeof repairTickets | typeof repairBatches,
  column: typeof repairTickets.ticketNumber | typeof repairBatches.batchNumber,
): Promise<string> {
  for (let attempt = 0; attempt < 10; attempt++) {
    const num = String(Math.floor(100000 + Math.random() * 900000))
    const [existing] = await db
      .select({ id: table.id })
      .from(table)
      .where(eq(column, num))
      .limit(1)
    if (!existing) return num
  }
  return String(Math.floor(10000000 + Math.random() * 90000000))
}

export const RepairTicketService = {
  async create(db: PostgresJsDatabase<any>, input: RepairTicketCreateInput) {
    const ticketNumber = await generateUniqueNumber(
      db, repairTickets, repairTickets.ticketNumber,
    )

    const [ticket] = await db
      .insert(repairTickets)
      .values({
        ticketNumber,
        customerName: input.customerName,
        customerPhone: input.customerPhone,
        accountId: input.accountId,
        locationId: input.locationId,
        repairBatchId: input.repairBatchId,
        inventoryUnitId: input.inventoryUnitId,
        itemDescription: input.itemDescription,
        serialNumber: input.serialNumber,
        conditionIn: input.conditionIn,
        conditionInNotes: input.conditionInNotes,
        problemDescription: input.problemDescription,
        technicianNotes: input.technicianNotes,
        assignedTechnicianId: input.assignedTechnicianId,
        estimatedCost: input.estimatedCost?.toString(),
        promisedDate: input.promisedDate ? new Date(input.promisedDate) : undefined,
      })
      .returning()
    return ticket
  },

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

  async list(db: PostgresJsDatabase<any>, params: PaginationInput, filters?: {
    status?: string[]
    conditionIn?: string[]
    isBatch?: boolean
    batchNumber?: string
    intakeDateFrom?: string
    intakeDateTo?: string
    promisedDateFrom?: string
    promisedDateTo?: string
    completedDateFrom?: string
    completedDateTo?: string
  }) {
    const conditions: SQL[] = []

    if (params.q) {
      const search = buildSearchCondition(params.q, [
        repairTickets.ticketNumber,
        repairTickets.customerName,
        repairTickets.customerPhone,
        repairTickets.itemDescription,
        repairTickets.serialNumber,
      ])
      if (search) conditions.push(search)
    }

    if (filters?.status?.length) {
      conditions.push(inArray(repairTickets.status, filters.status as any))
    }
    if (filters?.conditionIn?.length) {
      conditions.push(inArray(repairTickets.conditionIn, filters.conditionIn as any))
    }
    if (filters?.isBatch === true) {
      conditions.push(isNotNull(repairTickets.repairBatchId))
    } else if (filters?.isBatch === false) {
      conditions.push(isNull(repairTickets.repairBatchId))
    }
    if (filters?.intakeDateFrom) conditions.push(gte(repairTickets.intakeDate, new Date(filters.intakeDateFrom)))
    if (filters?.intakeDateTo) conditions.push(lte(repairTickets.intakeDate, new Date(filters.intakeDateTo)))
    if (filters?.promisedDateFrom) conditions.push(gte(repairTickets.promisedDate, new Date(filters.promisedDateFrom)))
    if (filters?.promisedDateTo) conditions.push(lte(repairTickets.promisedDate, new Date(filters.promisedDateTo)))
    if (filters?.completedDateFrom) conditions.push(gte(repairTickets.completedDate, new Date(filters.completedDateFrom)))
    if (filters?.completedDateTo) conditions.push(lte(repairTickets.completedDate, new Date(filters.completedDateTo)))

    const where = conditions.length > 0 ? and(...conditions) : undefined

    const sortableColumns: Record<string, Column> = {
      ticket_number: repairTickets.ticketNumber,
      customer_name: repairTickets.customerName,
      status: repairTickets.status,
      intake_date: repairTickets.intakeDate,
      promised_date: repairTickets.promisedDate,
      created_at: repairTickets.createdAt,
    }

    let query = db.select().from(repairTickets).where(where).$dynamic()
    query = withSort(query, params.sort, params.order, sortableColumns, repairTickets.createdAt)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(repairTickets).where(where),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async listByBatch(db: PostgresJsDatabase<any>, batchId: string, params: PaginationInput) {
    const baseWhere = eq(repairTickets.repairBatchId, batchId)
    const searchCondition = params.q
      ? buildSearchCondition(params.q, [repairTickets.ticketNumber, repairTickets.customerName, repairTickets.itemDescription])
      : undefined
    const where = searchCondition ? and(baseWhere, searchCondition) : baseWhere

    const sortableColumns: Record<string, Column> = {
      ticket_number: repairTickets.ticketNumber,
      customer_name: repairTickets.customerName,
      status: repairTickets.status,
      created_at: repairTickets.createdAt,
    }

    let query = db.select().from(repairTickets).where(where).$dynamic()
    query = withSort(query, params.sort, params.order, sortableColumns, repairTickets.createdAt)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(repairTickets).where(where),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async listReadyForPickup(db: PostgresJsDatabase<any>, params: PaginationInput) {
    const baseWhere = eq(repairTickets.status, 'ready')
    const searchCondition = params.q
      ? buildSearchCondition(params.q, [repairTickets.ticketNumber, repairTickets.customerName, repairTickets.customerPhone])
      : undefined
    const where = searchCondition ? and(baseWhere, searchCondition) : baseWhere

    let query = db.select().from(repairTickets).where(where).$dynamic()
    query = withSort(query, params.sort, params.order, { ticket_number: repairTickets.ticketNumber, customer_name: repairTickets.customerName, created_at: repairTickets.createdAt }, repairTickets.createdAt)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(repairTickets).where(where),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async update(db: PostgresJsDatabase<any>, id: string, input: RepairTicketUpdateInput) {
    const values: Record<string, unknown> = { ...input, updatedAt: new Date() }
    if (input.estimatedCost !== undefined) values.estimatedCost = input.estimatedCost.toString()
    if (input.promisedDate !== undefined) values.promisedDate = input.promisedDate ? new Date(input.promisedDate) : null

    const [ticket] = await db
      .update(repairTickets)
      .set(values)
      .where(eq(repairTickets.id, id))
      .returning()
    return ticket ?? null
  },

  async updateStatus(db: PostgresJsDatabase<any>, id: string, status: string) {
    const updates: Record<string, unknown> = { status, updatedAt: new Date() }
    if (status === 'ready' || status === 'picked_up' || status === 'delivered') {
      updates.completedDate = new Date()
    }

    const [ticket] = await db
      .update(repairTickets)
      .set(updates)
      .where(eq(repairTickets.id, id))
      .returning()
    return ticket ?? null
  },

  async delete(db: PostgresJsDatabase<any>, id: string) {
    // Soft-cancel: set status to cancelled rather than hard delete
    const [ticket] = await db
      .update(repairTickets)
      .set({ status: 'cancelled', updatedAt: new Date() })
      .where(eq(repairTickets.id, id))
      .returning()
    return ticket ?? null
  },
}

export const RepairLineItemService = {
  async create(db: PostgresJsDatabase<any>, input: RepairLineItemCreateInput) {
    const [item] = await db
      .insert(repairLineItems)
      .values({
        repairTicketId: input.repairTicketId,
        itemType: input.itemType,
        description: input.description,
        productId: input.productId,
        qty: input.qty?.toString(),
        unitPrice: input.unitPrice?.toString(),
        totalPrice: input.totalPrice?.toString(),
        cost: input.cost?.toString(),
        technicianId: input.technicianId,
      })
      .returning()
    return item
  },

  async listByTicket(db: PostgresJsDatabase<any>, ticketId: string, params: PaginationInput) {
    const where = eq(repairLineItems.repairTicketId, ticketId)

    const sortableColumns: Record<string, Column> = {
      item_type: repairLineItems.itemType,
      created_at: repairLineItems.createdAt,
    }

    let query = db.select().from(repairLineItems).where(where).$dynamic()
    query = withSort(query, params.sort, params.order, sortableColumns, repairLineItems.createdAt)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(repairLineItems).where(where),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async update(db: PostgresJsDatabase<any>, id: string, input: RepairLineItemUpdateInput) {
    const values: Record<string, unknown> = { ...input }
    if (input.qty !== undefined) values.qty = input.qty.toString()
    if (input.unitPrice !== undefined) values.unitPrice = input.unitPrice.toString()
    if (input.totalPrice !== undefined) values.totalPrice = input.totalPrice.toString()
    if (input.cost !== undefined) values.cost = input.cost.toString()

    const [item] = await db
      .update(repairLineItems)
      .set(values)
      .where(eq(repairLineItems.id, id))
      .returning()
    return item ?? null
  },

  async delete(db: PostgresJsDatabase<any>, id: string) {
    const [item] = await db
      .delete(repairLineItems)
      .where(eq(repairLineItems.id, id))
      .returning()
    return item ?? null
  },
}

export const RepairBatchService = {
  async create(db: PostgresJsDatabase<any>, input: RepairBatchCreateInput) {
    const batchNumber = await generateUniqueNumber(
      db, repairBatches, repairBatches.batchNumber,
    )

    const [batch] = await db
      .insert(repairBatches)
      .values({
        batchNumber,
        accountId: input.accountId,
        locationId: input.locationId,
        contactName: input.contactName,
        contactPhone: input.contactPhone,
        contactEmail: input.contactEmail,
        pickupDate: input.pickupDate ? new Date(input.pickupDate) : undefined,
        dueDate: input.dueDate ? new Date(input.dueDate) : undefined,
        itemCount: input.itemCount,
        notes: input.notes,
      })
      .returning()
    return batch
  },

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

  async list(db: PostgresJsDatabase<any>, params: PaginationInput) {
    const searchCondition = params.q
      ? buildSearchCondition(params.q, [repairBatches.batchNumber, repairBatches.contactName, repairBatches.contactEmail])
      : undefined

    const sortableColumns: Record<string, Column> = {
      batch_number: repairBatches.batchNumber,
      status: repairBatches.status,
      due_date: repairBatches.dueDate,
      created_at: repairBatches.createdAt,
    }

    let query = db.select().from(repairBatches).where(searchCondition).$dynamic()
    query = withSort(query, params.sort, params.order, sortableColumns, repairBatches.createdAt)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(repairBatches).where(searchCondition),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async update(db: PostgresJsDatabase<any>, id: string, input: RepairBatchUpdateInput) {
    const values: Record<string, unknown> = { ...input, updatedAt: new Date() }
    if (input.pickupDate !== undefined) values.pickupDate = input.pickupDate ? new Date(input.pickupDate) : null
    if (input.dueDate !== undefined) values.dueDate = input.dueDate ? new Date(input.dueDate) : null

    const [batch] = await db
      .update(repairBatches)
      .set(values)
      .where(eq(repairBatches.id, id))
      .returning()
    return batch ?? null
  },

  async updateStatus(db: PostgresJsDatabase<any>, id: string, status: string) {
    const updates: Record<string, unknown> = { status, updatedAt: new Date() }
    if (status === 'completed') updates.completedDate = new Date()
    if (status === 'delivered') updates.deliveredDate = new Date()

    const [batch] = await db
      .update(repairBatches)
      .set(updates)
      .where(eq(repairBatches.id, id))
      .returning()
    return batch ?? null
  },

  async approve(db: PostgresJsDatabase<any>, id: string, approvedBy: string) {
    const [batch] = await db
      .update(repairBatches)
      .set({
        approvalStatus: 'approved',
        approvedBy,
        approvedAt: new Date(),
        updatedAt: new Date(),
      })
      .where(eq(repairBatches.id, id))
      .returning()
    return batch ?? null
  },

  async reject(db: PostgresJsDatabase<any>, id: string) {
    const [batch] = await db
      .update(repairBatches)
      .set({
        approvalStatus: 'rejected',
        updatedAt: new Date(),
      })
      .where(eq(repairBatches.id, id))
      .returning()
    return batch ?? null
  },
}

export const RepairServiceTemplateService = {
  async create(db: PostgresJsDatabase<any>, input: RepairServiceTemplateCreateInput) {
    const [template] = await db
      .insert(repairServiceTemplates)
      .values({
        name: input.name,
        itemCategory: input.itemCategory,
        size: input.size,
        description: input.description,
        itemType: input.itemType,
        defaultPrice: input.defaultPrice?.toString(),
        defaultCost: input.defaultCost?.toString(),
        sortOrder: input.sortOrder,
      })
      .returning()
    return template
  },

  async list(db: PostgresJsDatabase<any>, params: PaginationInput) {
    const baseWhere = eq(repairServiceTemplates.isActive, true)
    const searchCondition = params.q
      ? buildSearchCondition(params.q, [repairServiceTemplates.name, repairServiceTemplates.itemCategory, repairServiceTemplates.size, repairServiceTemplates.description])
      : undefined
    const where = searchCondition ? and(baseWhere, searchCondition) : baseWhere

    const sortableColumns: Record<string, Column> = {
      name: repairServiceTemplates.name,
      item_category: repairServiceTemplates.itemCategory,
      default_price: repairServiceTemplates.defaultPrice,
      sort_order: repairServiceTemplates.sortOrder,
      created_at: repairServiceTemplates.createdAt,
    }

    let query = db.select().from(repairServiceTemplates).where(where).$dynamic()
    query = withSort(query, params.sort, params.order, sortableColumns, repairServiceTemplates.sortOrder)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(repairServiceTemplates).where(where),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async update(db: PostgresJsDatabase<any>, id: string, input: RepairServiceTemplateUpdateInput) {
    const values: Record<string, unknown> = { ...input, updatedAt: new Date() }
    if (input.defaultPrice !== undefined) values.defaultPrice = input.defaultPrice.toString()
    if (input.defaultCost !== undefined) values.defaultCost = input.defaultCost.toString()

    const [template] = await db
      .update(repairServiceTemplates)
      .set(values)
      .where(eq(repairServiceTemplates.id, id))
      .returning()
    return template ?? null
  },

  async delete(db: PostgresJsDatabase<any>, id: string) {
    const [template] = await db
      .update(repairServiceTemplates)
      .set({ isActive: false, updatedAt: new Date() })
      .where(eq(repairServiceTemplates.id, id))
      .returning()
    return template ?? null
  },
}

export const RepairNoteService = {
  async create(db: PostgresJsDatabase<any>, ticketId: string, authorId: string, authorName: string, ticketStatus: string, input: RepairNoteCreateInput) {
    const [note] = await db
      .insert(repairNotes)
      .values({
        repairTicketId: ticketId,
        authorId,
        authorName,
        content: input.content,
        visibility: input.visibility,
        ticketStatus: ticketStatus as any,
      })
      .returning()
    return note
  },

  async listByTicket(db: PostgresJsDatabase<any>, ticketId: string, params: PaginationInput) {
    const baseWhere = eq(repairNotes.repairTicketId, ticketId)
    const searchCondition = params.q
      ? buildSearchCondition(params.q, [repairNotes.content, repairNotes.authorName])
      : undefined
    const where = searchCondition ? and(baseWhere, searchCondition) : baseWhere

    const sortableColumns: Record<string, Column> = {
      created_at: repairNotes.createdAt,
      author_name: repairNotes.authorName,
    }

    let query = db.select().from(repairNotes).where(where).$dynamic()
    query = withSort(query, params.sort, params.order, sortableColumns, repairNotes.createdAt)
    query = withPagination(query, params.page, params.limit)

    const [data, [{ total }]] = await Promise.all([
      query,
      db.select({ total: count() }).from(repairNotes).where(where),
    ])

    return paginatedResponse(data, total, params.page, params.limit)
  },

  async delete(db: PostgresJsDatabase<any>, id: string) {
    const [note] = await db
      .delete(repairNotes)
      .where(eq(repairNotes.id, id))
      .returning()
    return note ?? null
  },
}
