import { computed, ref } from 'vue'
import { useMutation } from '@tanstack/vue-query'
import type { KbxLookupItem, KbxProblem, KbxValidationError } from '@kbx/contracts'
import { useKbxDirtyState, useKbxValidation } from '@kbx/ui'
import { itemPriceApi, toKbxProblem } from './itemPriceApi'
import type { ItemPriceRow } from './item-price.definition'

function localDate() {
  const d=new Date(); const m=String(d.getMonth()+1).padStart(2,'0'); const day=String(d.getDate()).padStart(2,'0')
  return `${d.getFullYear()}-${m}-${day}`
}
function newRow():ItemPriceRow { return { clientId:crypto.randomUUID(), itemId:null, itemCode:'', itemName:'', effectiveDate:localDate(), unitPrice:0, remark:'' } }

export function useItemPriceFastEntry() {
  const rows=ref<ItemPriceRow[]>(Array.from({length:8},()=>newRow()))
  const validation=useKbxValidation()
  const { dirty, touch, markSaved, reset:resetDirty }=useKbxDirtyState()
  const mutation=useMutation({ mutationFn:({request,key}:{request:{rows:Array<{clientId:string;itemId:string;effectiveDate:string;unitPrice:number;remark?:string}>};key:string})=>itemPriceApi.save(request,key) })
  const lastResult=ref<{requested:number;saved:number;created:number;updated:number}|null>(null)
  const problem=ref<KbxProblem|null>(null)
  let failedAction:'save'|null=null
  let idempotencyKey=crypto.randomUUID()

  const enteredRows=computed(()=>rows.value.filter(x=>x.itemId||x.itemCode.trim()||Number(x.unitPrice)!==0||x.remark.trim()))
  function invalidateCommandKey(){ idempotencyKey=crypto.randomUUID() }
  function addRow(){ rows.value.push(newRow()); validation.clear(); touch(); invalidateCommandKey() }
  function duplicateRows(source:ItemPriceRow[]){ rows.value.push(...source.map(x=>({...x,clientId:crypto.randomUUID()}))); touch(); invalidateCommandKey() }
  function clear(){ rows.value=Array.from({length:8},()=>newRow()); validation.clear(); lastResult.value=null; problem.value=null; failedAction=null; resetDirty(); invalidateCommandKey() }

  function localErrors():KbxValidationError[] {
    const errors:KbxValidationError[]=[]
    const active=enteredRows.value
    if(!active.length) return [{code:'PRICE_ROWS_REQUIRED',message:'저장할 단가를 한 건 이상 입력하세요.'}]
    const duplicate=new Map<string,string>()
    for(const row of active){
      if(!row.itemId) errors.push({rowKey:row.clientId,field:'itemCode',code:'ITEM_REQUIRED',message:'품목을 선택하세요.'})
      if(!/^\d{4}-\d{2}-\d{2}$/.test(row.effectiveDate)) errors.push({rowKey:row.clientId,field:'effectiveDate',code:'EFFECTIVE_DATE_REQUIRED',message:'적용일을 YYYY-MM-DD 형식으로 입력하세요.'})
      if(!Number.isFinite(Number(row.unitPrice))||Number(row.unitPrice)<0) errors.push({rowKey:row.clientId,field:'unitPrice',code:'PRICE_NONNEGATIVE',message:'단가는 0 이상이어야 합니다.'})
      if(row.itemId){ const key=`${row.itemId}|${row.effectiveDate}`; if(duplicate.has(key)){ errors.push({rowKey:row.clientId,field:'effectiveDate',code:'DUPLICATE_ITEM_DATE',message:'같은 품목과 적용일이 중복되었습니다.'}); const first=duplicate.get(key)!; if(!errors.some(x=>x.rowKey===first&&x.code==='DUPLICATE_ITEM_DATE')) errors.push({rowKey:first,field:'effectiveDate',code:'DUPLICATE_ITEM_DATE',message:'같은 품목과 적용일이 중복되었습니다.'}) } else duplicate.set(key,row.clientId) }
    }
    return errors
  }

  async function onCellChanged(event:{row:ItemPriceRow;field:keyof ItemPriceRow;newValue:unknown}){
    if(event.field==='itemCode'){
      const code=String(event.newValue??'').trim()
      if(!code) Object.assign(event.row,{itemId:null,itemName:''})
      else {
        const item=await itemPriceApi.resolveItemByCode(code)
        if(!item){ Object.assign(event.row,{itemId:null,itemName:''}); validation.setRowFieldError(event.row.clientId,'itemCode',{code:'ITEM_NOT_FOUND',message:'존재하지 않는 품목코드입니다. F2로 품목을 조회하세요.'}) }
        else { Object.assign(event.row,{itemId:item.id,itemCode:item.code,itemName:item.displayName}); validation.setRowFieldError(event.row.clientId,'itemCode') }
      }
    }
    lastResult.value=null; touch(); invalidateCommandKey()
  }
  function applyItemLookup(row:ItemPriceRow,item:KbxLookupItem<string>){ Object.assign(row,{itemId:item.id,itemCode:item.code,itemName:item.displayName}); validation.setRowFieldError(row.clientId,'itemCode'); touch(); invalidateCommandKey() }

  async function save(){
    validation.clear(); problem.value=null; failedAction=null; const errors=localErrors(); if(errors.length){validation.setErrors(errors);return false}
    const active=enteredRows.value
    try{
      const result=await mutation.mutateAsync({key:idempotencyKey,request:{rows:active.map(x=>({clientId:x.clientId,itemId:x.itemId!,effectiveDate:x.effectiveDate,unitPrice:Number(x.unitPrice),remark:x.remark||undefined}))}})
      lastResult.value=result; markSaved(); idempotencyKey=crypto.randomUUID(); return true
    }catch(error){ const issue=toKbxProblem(error); if(issue?.type==='validation'){validation.applyProblem(issue);return false} if(issue){problem.value=issue;failedAction='save';return false} throw error }
  }
  async function retryProblem(){const action=failedAction;problem.value=null;failedAction=null;if(action==='save')return save();return false}
  function dismissProblem(){problem.value=null;failedAction=null}
  return {rows,errors:validation.errors,dirty,saving:computed(()=>mutation.isPending.value),lastResult,problem,enteredCount:computed(()=>enteredRows.value.length),addRow,duplicateRows,onCellChanged,applyItemLookup,save,clear,touch,retryProblem,dismissProblem}
}
