'use client'

import { useCallback, useState } from 'react'
import { api } from '@/lib/api'
import { ENDPOINTS } from '@/constants/endpoints'

type Query = Record<string, string | number | undefined | null>

function unwrapList<T>(payload: any): T[] {
  if (Array.isArray(payload)) return payload
  if (Array.isArray(payload?.items)) return payload.items
  if (Array.isArray(payload?.data)) return payload.data
  if (Array.isArray(payload?.data?.items)) return payload.data.items
  return []
}

function errMessage(error: unknown, fallback: string) {
  return error instanceof Error ? error.message : fallback
}

export type PaymentStatus = 'draft' | 'pending' | 'approved' | 'posted' | 'cancelled' | 'refunded'
export type PaymentType = 'inbound' | 'outbound'
export type PaymentMethod = 'cash' | 'bank_transfer' | 'wallet'
export type PartyType = 'customer' | 'supplier' | 'platform'

export type SprintPayment = {
  id: string
  payment_no: string
  payment_type: PaymentType
  payment_method: PaymentMethod
  amount: number
  currency_id: string | null
  exchange_rate: number
  reference: string | null
  status: PaymentStatus
  payer_type: string | null
  payer_id: string | null
  payee_type: string | null
  payee_id: string | null
  description: string | null
  posted_at: string | null
  posting_journal_id: string | null
  reconciled_at: string | null
  reconciliation_id: string | null
  created_by: string | null
  reviewed_by: string | null
  reviewed_at: string | null
  approved_by: string | null
  approved_at: string | null
  cancelled_at: string | null
  cancellation_reason: string | null
  refunded_at: string | null
  refund_reason: string | null
  notes: string | null
  created_at: string | null
  updated_at: string | null
}

export type PaymentCreatePayload = {
  payment_type: PaymentType
  payment_method: PaymentMethod
  amount: number
  currency_id: string
  exchange_rate?: number
  reference?: string | null
  payer_type?: PartyType | null
  payer_id?: string | null
  payee_type?: PartyType | null
  payee_id?: string | null
  description?: string | null
  notes?: string | null
}

export type Voucher = {
  id: string
  voucher_no: string
  payment_id: string
  amount: number
  currency_id: string | null
  journal_header_id: string | null
  status: string
  issued_at: string | null
  issued_by: string | null
  notes: string | null
  created_at: string | null
  updated_at: string | null
}

export type SettlementLine = {
  id: string
  settlement_id: string
  reference_type: string
  reference_id: string
  amount: number
  status: string
  created_at: string | null
}

export type Settlement = {
  id: string
  settlement_no: string
  payment_id: string
  total_applied: number
  remaining_amount: number
  status: string
  posted_at: string | null
  posting_journal_id: string | null
  settled_by: string | null
  settled_at: string | null
  reversed_at: string | null
  reversal_reason: string | null
  notes: string | null
  created_by: string | null
  created_at: string | null
  updated_at: string | null
  lines: SettlementLine[]
}

export type Reconciliation = {
  id: string
  reconciliation_no: string
  reconciliation_type: 'cash' | 'bank' | 'wallet'
  period_start: string
  period_end: string
  expected_balance: number
  actual_balance: number
  difference: number
  status: string
  reconciled_by: string | null
  reconciled_at: string | null
  notes: string | null
  created_by: string | null
  created_at: string | null
  updated_at: string | null
}

export function useSprintPayments() {
  const [items, setItems] = useState<SprintPayment[]>([])
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const fetchPayments = useCallback(async (query: Query = {}) => {
    setIsLoading(true)
    setError(null)
    try {
      const payload = await api.get(ENDPOINTS.PAYMENTS.LIST, { params: { skip: 0, limit: 200, ...query } })
      setItems(unwrapList<SprintPayment>(payload))
    } catch (error) {
      setItems([])
      setError(errMessage(error, 'Failed to load payments'))
    } finally {
      setIsLoading(false)
    }
  }, [])

  const createPayment = useCallback(async (body: PaymentCreatePayload) => {
    const created = await api.post(ENDPOINTS.PAYMENTS.CREATE, body)
    return created as SprintPayment
  }, [])

  const transition = useCallback(async (url: string, body?: unknown) => {
    return api.post(url, body)
  }, [])

  const fetchVoucher = useCallback(async (payment: SprintPayment) => {
    const url = payment.payment_type === 'inbound'
      ? ENDPOINTS.PAYMENTS.RECEIPT_VOUCHER(payment.id)
      : ENDPOINTS.PAYMENTS.PAYMENT_VOUCHER(payment.id)
    return api.get(url) as Promise<Voucher | null>
  }, [])

  return {
    items,
    isLoading,
    error,
    fetchPayments,
    createPayment,
    fetchVoucher,
    submitPayment: (id: string, note?: string) => transition(ENDPOINTS.PAYMENTS.SUBMIT(id), { note }),
    approvePayment: (id: string, note?: string) => transition(ENDPOINTS.PAYMENTS.APPROVE(id), { note }),
    cancelPayment: (id: string, reason: string) => transition(ENDPOINTS.PAYMENTS.CANCEL(id), { reason }),
    postPayment: (id: string) => transition(ENDPOINTS.PAYMENTS.POST(id)),
    reversePayment: (id: string, reason: string) => transition(ENDPOINTS.PAYMENTS.REVERSE(id), { reason }),
  }
}

export function useSprintSettlements() {
  const [items, setItems] = useState<Settlement[]>([])
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const fetchSettlements = useCallback(async (query: Query = {}) => {
    setIsLoading(true)
    setError(null)
    try {
      const payload = await api.get(ENDPOINTS.SETTLEMENTS.LIST, { params: { skip: 0, limit: 200, ...query } })
      setItems(unwrapList<Settlement>(payload))
    } catch (error) {
      setItems([])
      setError(errMessage(error, 'Failed to load settlements'))
    } finally {
      setIsLoading(false)
    }
  }, [])

  return {
    items,
    isLoading,
    error,
    fetchSettlements,
    createSettlement: (payment_id: string, notes?: string) => api.post(ENDPOINTS.SETTLEMENTS.CREATE, { payment_id, notes }) as Promise<Settlement>,
    addLine: (settlementId: string, reference_type: string, reference_id: string, amount: number) =>
      api.post(ENDPOINTS.SETTLEMENTS.LINES(settlementId), { reference_type, reference_id, amount }) as Promise<SettlementLine>,
    submitSettlement: (id: string) => api.post(ENDPOINTS.SETTLEMENTS.SUBMIT(id)),
    approveSettlement: (id: string) => api.post(ENDPOINTS.SETTLEMENTS.APPROVE(id)),
    postSettlement: (id: string) => api.post(ENDPOINTS.SETTLEMENTS.POST(id)),
    reverseSettlement: (id: string, reason: string) => api.post(ENDPOINTS.SETTLEMENTS.REVERSE(id), { reason }),
  }
}

export function useSprintReconciliations() {
  const [items, setItems] = useState<Reconciliation[]>([])
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)

  const fetchReconciliations = useCallback(async (query: Query = {}) => {
    setIsLoading(true)
    setError(null)
    try {
      const payload = await api.get(ENDPOINTS.SPRINT_RECONCILIATIONS.LIST, { params: { skip: 0, limit: 200, ...query } })
      setItems(unwrapList<Reconciliation>(payload))
    } catch (error) {
      setItems([])
      setError(errMessage(error, 'Failed to load reconciliations'))
    } finally {
      setIsLoading(false)
    }
  }, [])

  return {
    items,
    isLoading,
    error,
    fetchReconciliations,
    createReconciliation: (body: Pick<Reconciliation, 'reconciliation_type' | 'period_start' | 'period_end'> & { actual_balance: number; notes?: string | null }) =>
      api.post(ENDPOINTS.SPRINT_RECONCILIATIONS.CREATE, body) as Promise<Reconciliation>,
    startReconciliation: (id: string) => api.post(ENDPOINTS.SPRINT_RECONCILIATIONS.START(id)),
    markReconciled: (id: string) => api.post(ENDPOINTS.SPRINT_RECONCILIATIONS.MARK_RECONCILED(id)),
    markDiscrepancy: (id: string, notes: string) => api.post(ENDPOINTS.SPRINT_RECONCILIATIONS.MARK_DISCREPANCY(id), undefined, { params: { notes } }),
  }
}
