import { useCallback, useState } from "react"
import { api } from "@/lib/api"
import { ENDPOINTS } from "@/constants/endpoints"
import { createEntityHook } from "./useEntityBase"
import type {
  ReceivingBatchCompletePayload,
  ReceivingBatchCreatePayload,
  ReceivingBatchRead,
  AssayResultCreatePayload,
  ReceivingOrderCreatePayload,
  ReceivingOrderRead,
  ReceivingOrderStatusHistoryRead,
  ReceivingOrderUpdatePayload,
  ReceivingOrderWorkflowAction,
  ReceivingPostingPreview,
  ReceivingPostingResult,
  ReceivingReceiptRead,
} from "@/types"

export const useReceivingBase = createEntityHook<
  ReceivingBatchRead,
  ReceivingBatchCreatePayload,
  never
>("Receiving batches", ENDPOINTS.RECEIVING.LIST)

export const useReceivingOrdersBase = createEntityHook<
  ReceivingOrderRead,
  ReceivingOrderCreatePayload,
  ReceivingOrderUpdatePayload
>("Receiving orders", ENDPOINTS.RECEIVING_ORDERS.LIST)

function formatFieldErrors(fieldErrors: unknown): string {
  if (!fieldErrors || typeof fieldErrors !== "object") return ""
  return Object.entries(fieldErrors as Record<string, unknown>)
    .flatMap(([field, errors]) => {
      const values = Array.isArray(errors) ? errors : [errors]
      return values
        .filter((value): value is string => typeof value === "string" && value.trim().length > 0)
        .map((value) => `${field}: ${value}`)
    })
    .slice(0, 4)
    .join("\n")
}

function formatDetail(detail: unknown): string {
  if (typeof detail === "string") return detail
  if (!Array.isArray(detail)) return ""
  return detail
    .map((entry) => {
      if (!entry || typeof entry !== "object") return ""
      const item = entry as { loc?: unknown[]; msg?: unknown }
      const field = Array.isArray(item.loc) ? item.loc.filter((part) => part !== "body").join(".") : ""
      const msg = typeof item.msg === "string" ? item.msg : ""
      return field && msg ? `${field}: ${msg}` : msg
    })
    .filter(Boolean)
    .slice(0, 4)
    .join("\n")
}

function getErrorMessage(err: unknown, fallback: string) {
  if (err && typeof err === "object" && "response" in err) {
    const data = (err as { response?: { data?: unknown } }).response?.data
    if (data && typeof data === "object") {
      const payload = data as { message?: unknown; detail?: unknown; field_errors?: unknown }
      const fieldMessage = formatFieldErrors(payload.field_errors)
      if (fieldMessage) return fieldMessage
      if (typeof payload.message === "string" && payload.message.trim()) return payload.message
      const detailMessage = formatDetail(payload.detail)
      if (detailMessage) return detailMessage
    }
  }
  if (err instanceof Error) return err.message || fallback
  return fallback
}

function unwrap<T>(response: unknown): T {
  if (response && typeof response === "object" && "success" in response) {
    const wrapped = response as { data?: T }
    return wrapped.data as T
  }
  return response as T
}

export function useReceiving() {
  const base = useReceivingBase()
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [actionError, setActionError] = useState<string | null>(null)

  const refresh = useCallback(async () => {
    await base.fetchItems(base.currentPage, base.pageSize || 100, base.searchTerm)
  }, [base.currentPage, base.fetchItems, base.pageSize, base.searchTerm])

  const startInspection = useCallback(async (id: string) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      await api.post(ENDPOINTS.RECEIVING.START_INSPECTION(id))
      await refresh()
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to start inspection."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [refresh])

  const completeBatch = useCallback(async (id: string, payload: ReceivingBatchCompletePayload) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      await api.post(ENDPOINTS.RECEIVING.COMPLETE(id), undefined, {
        params: {
          actual_total_bars: payload.actual_total_bars,
          actual_total_weight: payload.actual_total_weight,
        },
      })
      await refresh()
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to complete receiving batch."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [refresh])

  return {
    ...base,
    isSubmitting,
    error: actionError || base.error,
    startInspection,
    completeBatch,
  }
}

function receivingOrderActionEndpoint(orderId: string, action: ReceivingOrderWorkflowAction) {
  switch (action) {
    case "submit-for-review":
      return ENDPOINTS.RECEIVING_ORDERS.SUBMIT_FOR_REVIEW(orderId)
    case "approve":
      return ENDPOINTS.RECEIVING_ORDERS.APPROVE(orderId)
    case "reject":
      return ENDPOINTS.RECEIVING_ORDERS.REJECT(orderId)
    case "mark-received":
      return ENDPOINTS.RECEIVING_ORDERS.MARK_RECEIVED(orderId)
    case "post":
      return ENDPOINTS.RECEIVING_ORDERS.POST(orderId)
    case "cancel":
      return ENDPOINTS.RECEIVING_ORDERS.CANCEL(orderId)
    case "resubmit":
      return ENDPOINTS.RECEIVING_ORDERS.RESUBMIT(orderId)
  }
}

export function useReceivingOrders() {
  const base = useReceivingOrdersBase()
  const [isSubmitting, setIsSubmitting] = useState(false)
  const [actionError, setActionError] = useState<string | null>(null)
  const [postingResult, setPostingResult] = useState<ReceivingPostingResult | null>(null)
  const [statusHistory, setStatusHistory] = useState<ReceivingOrderStatusHistoryRead[]>([])
  const [postingPreview, setPostingPreview] = useState<ReceivingPostingPreview | null>(null)
  const [receipt, setReceipt] = useState<ReceivingReceiptRead | null>(null)

  const refresh = useCallback(async () => {
    await base.fetchItems(base.currentPage, base.pageSize || 100, base.searchTerm)
  }, [base.currentPage, base.fetchItems, base.pageSize, base.searchTerm])

  const transitionOrder = useCallback(async (
    orderId: string,
    action: ReceivingOrderWorkflowAction,
    note?: string,
  ) => {
    setIsSubmitting(true)
    setActionError(null)
    setPostingResult(null)
    try {
      const body = action === "cancel" ? { reason: note } : action === "post" ? undefined : { note: note || null }
      
      const requiresIdempotencyKey = ["submit-for-review", "approve", "mark-received", "confirm", "send-to-inspection", "post", "cancel"].includes(action)
      const headers = requiresIdempotencyKey ? { "Idempotency-Key": (typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2)) } : undefined

      const response = await api.post(receivingOrderActionEndpoint(orderId, action), body, { headers })
      if (action === "post") {
        const value = response as { data?: ReceivingPostingResult }
        setPostingResult(value?.data ?? response ?? null)
      }
      await refresh()
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, `Failed to ${action.replace(/-/g, " ")} receiving order.`))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [refresh])

  const createOrder = useCallback(async (payload: ReceivingOrderCreatePayload) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      const response = await api.post(ENDPOINTS.RECEIVING_ORDERS.CREATE, payload)
      const order = unwrap<ReceivingOrderRead>(response)
      await refresh()
      return order
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to create receiving order."))
      return null
    } finally {
      setIsSubmitting(false)
    }
  }, [refresh])

  const fetchStatusHistory = useCallback(async (orderId: string) => {
    setActionError(null)
    try {
      const response = await api.get(ENDPOINTS.RECEIVING_ORDERS.STATUS_HISTORY(orderId))
      const rows = unwrap<ReceivingOrderStatusHistoryRead[]>(response)
      setStatusHistory(rows || [])
      return rows || []
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to load receiving order history."))
      setStatusHistory([])
      return []
    }
  }, [])

  const addAssayResult = useCallback(async (orderId: string, itemId: string, payload: AssayResultCreatePayload) => {
    setIsSubmitting(true)
    setActionError(null)
    try {
      await api.post(ENDPOINTS.RECEIVING_ORDERS.ITEM_ASSAY(orderId, itemId), payload, { headers: { "Idempotency-Key": (typeof crypto !== 'undefined' && crypto.randomUUID ? crypto.randomUUID() : Math.random().toString(36).substring(2)) } })
      await refresh()
      return true
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to record inspection result."))
      return false
    } finally {
      setIsSubmitting(false)
    }
  }, [refresh])

  const fetchPostingPreview = useCallback(async (orderId: string) => {
    setActionError(null)
    try {
      const response = await api.get(ENDPOINTS.RECEIVING_ORDERS.POSTING_PREVIEW(orderId))
      const preview = unwrap<ReceivingPostingPreview>(response)
      setPostingPreview(preview)
      return preview
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to load posting preview."))
      setPostingPreview(null)
      return null
    }
  }, [])

  const fetchReceipt = useCallback(async (orderId: string) => {
    setActionError(null)
    try {
      const response = await api.get(ENDPOINTS.RECEIVING_ORDERS.RECEIPT(orderId))
      const value = unwrap<ReceivingReceiptRead>(response)
      setReceipt(value)
      return value
    } catch (err) {
      setActionError(getErrorMessage(err, "Failed to load receiving receipt."))
      setReceipt(null)
      return null
    }
  }, [])

  return {
    ...base,
    isSubmitting,
    error: actionError || base.error,
    postingResult,
    statusHistory,
    postingPreview,
    receipt,
    createOrder,
    fetchStatusHistory,
    addAssayResult,
    fetchPostingPreview,
    fetchReceipt,
    transitionOrder,
  }
}
