<script setup lang="ts">
import { computed, nextTick, reactive, ref } from 'vue'
import type { KbxLookupItem, KbxValidationError } from '@kbx/contracts'
import {
  KbxButton,
  KbxDataGrid,
  KbxDateField,
  KbxFormGrid,
  KbxFormSection,
  KbxLookup,
  KbxLookupDialog,
  KbxTransactionPage,
  KbxUnsavedChangesDialog,
  KbxWorkflowBar,
  useKbxDirtyState,
  useKbxPageShortcuts,
} from '@kbx/ui'
import { useKbxWorkspaceBinding } from '../../../shell/useKbxWorkspaceBinding'
import { lookupRegistry } from '../../../lookups/lookupRegistry'
import { inventoryMoveColumns, inventoryMoveScreen, inventoryMoveWorkflow, type InventoryMoveLine } from './inventory-move.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():InventoryMoveLine{return{clientId:crypto.randomUUID(),itemId:null,itemCode:'',itemName:'',availableQty:0,moveQty:1,lotNo:'',remark:''}}

const status=ref('DRAFT')
const header=reactive({moveDate:today(),fromWarehouseId:null as string|null,toWarehouseId:null as string|null})
const lines=ref<InventoryMoveLine[]>([newLine()])
const errors=ref<KbxValidationError[]>([])
const pageNotice=ref('')
const detailGrid=ref<any>(null)
const itemLookupOpen=ref(false)
const activeLine=ref<InventoryMoveLine|null>(null)
const activeLookupField=ref<keyof InventoryMoveLine & 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.moveQty||0),0))
const summaryItems=computed(()=>[
  {key:'items',label:'품목',value:`${lines.value.length.toLocaleString('ko-KR')}종`},
  {key:'qty',label:'총 이동수량',value:totalQty.value,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 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:InventoryMoveLine[]){if(readonly.value||!selected.length)return;lines.value.push(...selected.map(line=>({...line,clientId:crypto.randomUUID()})));touch()}

function validateDraft(){
  const next:KbxValidationError[]=[]
  if(!header.fromWarehouseId)next.push({field:'fromWarehouseId',code:'FROM_WAREHOUSE_REQUIRED',message:'출발창고를 선택하세요.'})
  if(!header.toWarehouseId)next.push({field:'toWarehouseId',code:'TO_WAREHOUSE_REQUIRED',message:'도착창고를 선택하세요.'})
  if(header.fromWarehouseId&&header.fromWarehouseId===header.toWarehouseId)next.push({field:'toWarehouseId',code:'WAREHOUSE_MUST_DIFFER',message:'도착창고는 출발창고와 달라야 합니다.'})
  if(!lines.value.length)next.push({code:'MOVE_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.moveQty)<=0)next.push({rowKey:line.clientId,field:'moveQty',code:'MOVE_QTY_POSITIVE',message:'이동수량은 0보다 커야 합니다.'})
    if(Number(line.availableQty)>0&&Number(line.moveQty)>Number(line.availableQty))next.push({rowKey:line.clientId,field:'moveQty',code:'INSUFFICIENT_STOCK',message:`가용재고는 ${Number(line.availableQty).toLocaleString('ko-KR')}개입니다.`})
  }
  errors.value=next
  return next.length===0
}

async function onCellChanged(event:{row:InventoryMoveLine;field:keyof InventoryMoveLine & 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:'',availableQty:0})
    else{
      const item=await lookupRegistry.item.resolveByCode(code)
      if(item)Object.assign(event.row,{itemId:item.id,itemCode:item.code,itemName:item.displayName,availableQty:Number(item.metadata?.availableQty??0)})
      else{
        Object.assign(event.row,{itemId:null,itemName:'',availableQty:0})
        errors.value.push({rowKey:event.row.clientId,field:'itemCode',code:'ITEM_NOT_FOUND',message:'존재하지 않는 품목코드입니다. F2로 품목을 조회하세요.'})
      }
    }
  }
  touch()
}
function openGridLookup(event:{row:InventoryMoveLine;field:keyof InventoryMoveLine & 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,availableQty:Number(item.metadata?.availableQty??0)});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,{moveDate:today(),fromWarehouseId:null,toWarehouseId:null});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(dirty.value){pageNotice.value='상태 변경 전에 먼저 저장해야 합니다.';return}pageNotice.value=`${inventoryMoveWorkflow.transitions.find(item=>item.id===id)?.label??'상태 변경'} Command가 서버에 연결되지 않아 상태를 변경하지 않았습니다.`}
function command(id:string){if(id==='new')return requestNew();if(id==='save')return saveDraft();if(['confirm','ship','receive'].includes(id))return executeWorkflow(id);if(id==='excel')pageNotice.value='엑셀 기능은 공통 Import/Export 계약이 연결된 뒤 활성화합니다.'}
function focusNextError(){detailGrid.value?.focusError?.(1)}
useKbxPageShortcuts([{key:'F8',execute:saveDraft}])
</script>

<template>
  <KbxTransactionPage
    :screen="inventoryMoveScreen"
    :status="status"
    :dirty="dirty"
    :errors="errors"
    :workflow="inventoryMoveWorkflow"
    :summary-items="summaryItems"
    :context="pageContext"
    header-title="이동 정보"
    header-description="출발/도착 창고와 이동일을 먼저 확인합니다. 동일 창고 이동은 입력 단계에서 차단합니다."
    detail-title="이동 품목"
    :detail-count="lines.length"
    detail-count-unit="종"
    detail-description="가용재고와 이동수량을 비교하며 입력하고, 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="inventoryMoveWorkflow" :current="status" @transition="executeWorkflow" />
      <KbxFormSection title="이동정보">
        <KbxFormGrid>
          <KbxDateField v-model="header.moveDate" label="이동일" required :readonly="readonly" @update:model-value="touch" />
          <KbxLookup v-model="header.fromWarehouseId" entity="warehouse" label="출발창고" required :readonly="readonly" :error="fieldError('fromWarehouseId')" @selected="()=>{clearError(undefined,'fromWarehouseId');touch()}" />
          <KbxLookup v-model="header.toWarehouseId" entity="warehouse" label="도착창고" required :readonly="readonly" :error="fieldError('toWarehouseId')" @selected="()=>{clearError(undefined,'toWarehouseId');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="inventoryMoveColumns"
        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>
