<script setup lang="ts">
import { computed, nextTick, reactive, ref } from 'vue'
import type { KbxLookupItem, KbxValidationError } from '@kbx/contracts'
import {
  KbxButton,
  KbxDataGrid,
  KbxDateField,
  KbxFormGrid,
  KbxFormSection,
  KbxInput,
  KbxLookup,
  KbxLookupDialog,
  KbxTransactionPage,
  KbxUnsavedChangesDialog,
  KbxWorkflowBar,
  useKbxDirtyState,
  useKbxPageShortcuts,
} from '@kbx/ui'
import { useKbxWorkspaceBinding } from '../../../shell/useKbxWorkspaceBinding'
import { lookupRegistry } from '../../../lookups/lookupRegistry'
import { purchaseColumns, purchaseScreen, purchaseWorkflow, type PurchaseLine } from './purchase.definition'

function today() {
  const value = new Date()
  return `${value.getFullYear()}-${String(value.getMonth() + 1).padStart(2, '0')}-${String(value.getDate()).padStart(2, '0')}`
}
function newLine():PurchaseLine {
  return { clientId:crypto.randomUUID(), itemId:null, itemCode:'', itemName:'', quantity:1, unitPrice:0, amount:0, dueDate:'' }
}

const status=ref('DRAFT')
const header=reactive({ purchaseDate:today(), supplierId:null as string|null, warehouseId:null as string|null, buyer:'', remark:'' })
const lines=ref<PurchaseLine[]>([newLine()])
const errors=ref<KbxValidationError[]>([])
const pageNotice=ref('')
const detailGrid=ref<any>(null)
const itemLookupOpen=ref(false)
const activeLine=ref<PurchaseLine|null>(null)
const activeLookupField=ref<keyof PurchaseLine & string>('itemCode')
const pendingReset=ref(false)
const { dirty, touch, reset:resetDirty }=useKbxDirtyState()
useKbxWorkspaceBinding(dirty, async()=>false)

const readonly=computed(()=>status.value!=='DRAFT')
const totalQty=computed(()=>lines.value.reduce((sum,line)=>sum+Number(line.quantity||0),0))
const total=computed(()=>lines.value.reduce((sum,line)=>sum+Number(line.amount||0),0))
const summaryItems=computed(()=>[
  {key:'items',label:'품목',value:`${lines.value.length.toLocaleString('ko-KR')}종`},
  {key:'qty',label:'총수량',value:totalQty.value},
  {key:'amount',label:'총 구매금액',value:`₩${total.value.toLocaleString('ko-KR')}`,emphasis:true},
])
const pageContext=computed(()=>({
  label:'신규 구매',
  hint:dirty.value?'저장하지 않은 입력이 있습니다.':'작성 상태 · 서버 반영 전',
  metrics:[
    {key:'status',label:'상태',value:'작성'},
    {key:'lines',label:'품목',value:lines.value.length},
    {key:'errors',label:'오류',value:errors.value.length,tone:'danger' as const,emphasis:errors.value.length>0},
  ],
}))

function fieldError(field:string){return errors.value.find(error=>!error.rowKey&&error.field===field)?.message}
function clearError(rowKey:string|undefined,field:string){errors.value=errors.value.filter(error=>!(error.rowKey===rowKey&&error.field===field))}
function recalc(line:PurchaseLine){line.amount=Number(line.quantity||0)*Number(line.unitPrice||0)}
function addLine(){if(readonly.value)return;lines.value.push(newLine());touch()}
async function addLineAndFocus(){addLine();const line=lines.value.at(-1);if(!line)return;await nextTick();detailGrid.value?.focusCell?.(line.clientId,'itemCode')}
function duplicateLines(selected:PurchaseLine[]){if(readonly.value||!selected.length)return;lines.value.push(...selected.map(line=>({...line,clientId:crypto.randomUUID()})));touch()}

function validateDraft(){
  const next:KbxValidationError[]=[]
  if(!header.supplierId)next.push({field:'supplierId',code:'SUPPLIER_REQUIRED',message:'거래처를 선택하세요.'})
  if(!header.warehouseId)next.push({field:'warehouseId',code:'WAREHOUSE_REQUIRED',message:'입고창고를 선택하세요.'})
  if(!lines.value.length)next.push({code:'PURCHASE_LINE_REQUIRED',message:'구매 품목을 1건 이상 입력하세요.'})
  for(const line of lines.value){
    if(!line.itemId)next.push({rowKey:line.clientId,field:'itemCode',code:'ITEM_REQUIRED',message:'품목을 선택하세요.'})
    if(Number(line.quantity)<=0)next.push({rowKey:line.clientId,field:'quantity',code:'QUANTITY_POSITIVE',message:'수량은 0보다 커야 합니다.'})
    if(Number(line.unitPrice)<0)next.push({rowKey:line.clientId,field:'unitPrice',code:'UNIT_PRICE_NONNEGATIVE',message:'단가는 0 이상이어야 합니다.'})
  }
  errors.value=next
  return next.length===0
}

async function onCellChanged(event:{row:PurchaseLine;field:keyof PurchaseLine & string;newValue:unknown}){
  clearError(event.row.clientId,event.field)
  if(event.field==='itemCode'){
    const code=String(event.newValue??'').trim()
    if(!code)Object.assign(event.row,{itemId:null,itemName:''})
    else{
      const item=await lookupRegistry.item.resolveByCode(code)
      if(item)Object.assign(event.row,{itemId:item.id,itemCode:item.code,itemName:item.displayName})
      else{
        Object.assign(event.row,{itemId:null,itemName:''})
        errors.value.push({rowKey:event.row.clientId,field:'itemCode',code:'ITEM_NOT_FOUND',message:'존재하지 않는 품목코드입니다. F2로 품목을 조회하세요.'})
      }
    }
  }
  if(event.field==='quantity'||event.field==='unitPrice')recalc(event.row)
  touch()
}

function openGridLookup(event:{row:PurchaseLine;field:keyof PurchaseLine & string;entity:string}){
  if(readonly.value||event.entity!=='item')return
  activeLine.value=event.row
  activeLookupField.value=event.field
  itemLookupOpen.value=true
}
async function selectGridItem(item:KbxLookupItem<string>){
  if(!activeLine.value)return
  Object.assign(activeLine.value,{itemId:item.id,itemCode:item.code,itemName:item.displayName})
  clearError(activeLine.value.clientId,'itemCode')
  touch();itemLookupOpen.value=false
  await nextTick();detailGrid.value?.focusNextEditableCell?.(activeLine.value.clientId,activeLookupField.value)
}

function resetDraft(){
  status.value='DRAFT'
  Object.assign(header,{purchaseDate:today(),supplierId:null,warehouseId:null,buyer:'',remark:''})
  lines.value=[newLine()]
  errors.value=[];pageNotice.value='';pendingReset.value=false;resetDirty()
}
function requestNew(){if(dirty.value){pendingReset.value=true;return}resetDraft()}
function saveDraft(){
  pageNotice.value=''
  if(!validateDraft()){pageNotice.value='저장할 수 없습니다. 입력 오류를 확인하세요.';nextTick(()=>detailGrid.value?.focusError?.(1));return false}
  pageNotice.value='저장 Command가 아직 서버에 연결되지 않아 입력값을 반영하지 않았습니다. 화면 초안은 유지됩니다.'
  return false
}
function executeWorkflow(id:string){
  if(id==='confirm'){
    if(dirty.value){pageNotice.value='구매확정 전에 먼저 저장해야 합니다.';return}
    pageNotice.value='구매확정 Command가 서버에 연결되지 않아 상태를 변경하지 않았습니다.'
    return
  }
  pageNotice.value='현재 상태 변경은 서버 Domain Command가 연결된 뒤 실행할 수 있습니다.'
}
function command(id:string){
  if(id==='new')return requestNew()
  if(id==='save')return saveDraft()
  if(id==='confirm'||id==='cancel')return executeWorkflow(id)
  if(id==='excel')pageNotice.value='엑셀 메뉴는 공통 KbxExcelMenu 연결 후 활성화합니다. 임의 브라우저 Import는 수행하지 않습니다.'
}
function focusNextError(){detailGrid.value?.focusError?.(1)}

useKbxPageShortcuts([{key:'F8',execute:saveDraft}])
</script>

<template>
  <KbxTransactionPage
    :screen="purchaseScreen"
    :status="status"
    :dirty="dirty"
    :errors="errors"
    :workflow="purchaseWorkflow"
    :summary-items="summaryItems"
    :context="pageContext"
    header-title="구매 정보"
    header-description="거래처·입고창고·담당자 기준을 먼저 확정합니다."
    detail-title="구매 품목"
    :detail-count="lines.length"
    detail-count-unit="종"
    detail-description="수량·단가·납기일을 한 Grid에서 연속 입력합니다. F2로 품목을 조회할 수 있습니다."
    breadcrumb="ERP > 구매"
    @command="command"
    @transition="executeWorkflow"
  >
    <template #notice><div v-if="pageNotice" class="page-notice" role="status">{{pageNotice}}</div></template>
    <template #header>
      <KbxWorkflowBar :workflow="purchaseWorkflow" :current="status" @transition="executeWorkflow" />
      <KbxFormSection title="구매정보">
        <KbxFormGrid>
          <KbxDateField v-model="header.purchaseDate" label="구매일" required :readonly="readonly" @update:model-value="touch" />
          <KbxLookup v-model="header.supplierId" entity="customer" label="거래처" required :readonly="readonly" :error="fieldError('supplierId')" @selected="()=>{clearError(undefined,'supplierId');touch()}" />
          <KbxLookup v-model="header.warehouseId" entity="warehouse" label="입고창고" required :readonly="readonly" :error="fieldError('warehouseId')" @selected="()=>{clearError(undefined,'warehouseId');touch()}" />
          <KbxInput v-model="header.buyer" label="담당자" :readonly="readonly" @update:model-value="touch" />
        </KbxFormGrid>
      </KbxFormSection>
    </template>

    <template #detail-actions>
      <KbxButton v-if="errors.length" label="첫/다음 오류" variant="ghost" @click="focusNextError" />
      <KbxButton v-if="!readonly" label="행 추가" variant="secondary" @click="addLineAndFocus" />
    </template>
    <template #detail>
      <KbxDataGrid
        ref="detailGrid"
        :rows="lines"
        :columns="purchaseColumns"
        row-key="clientId"
        aria-label="구매 품목"
        selection="multiple"
        :errors="errors"
        :editable="!readonly"
        :editing-policy="{allowRowAdd:true,allowRowDuplicate:true,fillDown:true,paste:true,errorNavigation:true}"
        @row-add-requested="addLine"
        @row-duplicate-requested="duplicateLines"
        @cell-changed="onCellChanged"
        @lookup-requested="openGridLookup"
      />
    </template>
  </KbxTransactionPage>

  <KbxLookupDialog v-model:visible="itemLookupOpen" entity="item" title="품목" @select="selectGridItem" />
  <KbxUnsavedChangesDialog :open="pendingReset" :can-save="false" @stay="pendingReset=false" @discard="resetDraft" />
</template>

<style scoped>
.page-notice{padding:var(--kbx-space-2) var(--kbx-space-3);border:var(--kbx-border-width) solid var(--kbx-color-warning-border);background:var(--kbx-color-warning-surface);font-size:var(--kbx-font-sm)}
</style>
