<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import type {
  KbxImportDefinition,
  KbxImportMapping,
  KbxImportSession,
  KbxImportProgressEvent,
} from '@kbx/contracts'
import KbxButton from './KbxButton.vue'
import KbxConfirm from './KbxConfirm.vue'
import KbxJobProgress from './KbxJobProgress.vue'
import KbxProgressSteps from './KbxProgressSteps.vue'
import { validateKbxImportFileCandidate, validateKbxImportMappings } from '../excel/importGuard'

const props = defineProps<{
  definition: KbxImportDefinition
  session: KbxImportSession | null
  busy?: boolean
  progress?: KbxImportProgressEvent | null
}>()

const emit = defineEmits<{
  upload: [File]
  saveMapping: [KbxImportMapping[]]
  saveNamedMapping: [string, KbxImportMapping[]]
  validate: []
  commit: []
  downloadTemplate: []
  downloadErrors: []
  cancel: []
}>()

const fileInput = ref<HTMLInputElement | null>(null)
const localMapping = ref<KbxImportMapping[] | null>(null)
const dragActive = ref(false)
const mappingName = ref('')
const clientIssue = ref('')
const commitConfirmOpen = ref(false)
const importSteps=[{key:'file',label:'파일'},{key:'mapping',label:'매핑'},{key:'validation',label:'검증'},{key:'commit',label:'반영'}]
const currentStepKey=computed(()=>step.value===1?'file':step.value===2?'mapping':step.value===3?'validation':'commit')

const step = computed(() => {
  const status = props.session?.status
  if (!status || status === 'created') return 1
  if (status === 'uploaded' || status === 'mapping-required') return 2
  if (status === 'validating' || status === 'validated') return 3
  return 4
})

watch(() => props.session?.id, () => {
  localMapping.value = null
  mappingName.value = ''
  clientIssue.value = ''
  commitConfirmOpen.value = false
})

const mappings = computed({
  get: () => localMapping.value ?? (props.session?.mapping ?? []),
  set: value => { localMapping.value = value },
})

const importableFields = computed(() => props.definition.fields.filter(field => field.importable !== false))
const mappingIssues = computed(() => validateKbxImportMappings(props.definition,mappings.value))
const canValidate = computed(() => mappings.value.length > 0 && mappingIssues.value.length === 0 && !props.busy)
const canCommit = computed(() => (props.session?.validRows ?? 0) > 0 && props.session?.status === 'validated' && !props.busy)
const isFailed = computed(() => props.session?.status === 'failed')
const isCancelled = computed(() => props.session?.status === 'cancelled')
const isPartial = computed(() => props.session?.status === 'partially-completed')

function chooseFile() { clientIssue.value=''; fileInput.value?.click() }
function onFiles(files: FileList | null) {
  const file = files?.item(0)
  if (!file) return
  clientIssue.value=''
  const issue=validateKbxImportFileCandidate(props.definition,file)
  if(issue){clientIssue.value=issue;return}
  emit('upload', file)
}
function onDrop(event: DragEvent) {
  dragActive.value = false
  onFiles(event.dataTransfer?.files ?? null)
}
function setTarget(index: number, targetField: string) {
  const next = mappings.value.map((item, itemIndex) => itemIndex === index
    ? { ...item, targetField: targetField || null, source: 'manual' as const, confidence: undefined, reason:undefined }
    : item)
  mappings.value = next
  emit('saveMapping', next)
}
function requestCommit(){if(canCommit.value)commitConfirmOpen.value=true}
function confirmCommit(){commitConfirmOpen.value=false;emit('commit')}
</script>

<template>
  <section class="kbx-import" data-kbx-component="excel-import" :aria-busy="busy || undefined">
    <header class="kbx-import__header">
      <div>
        <h2>{{ definition.title }}</h2>
        <p>파일 → 매핑 → 검증 → 반영 순서로 처리합니다. 원본 행 번호와 오류 이력은 유지됩니다.</p>
      </div>
      <KbxButton label="업로드 양식 다운로드" variant="secondary" @click="emit('downloadTemplate')" />
    </header>

    <KbxProgressSteps data-kbx-surface="progress-steps" :steps="importSteps" :current="currentStepKey" />

    <div v-if="step === 1" class="kbx-import__drop" data-kbx-surface="file"
      :class="{ 'is-dragging': dragActive }"
      @dragover.prevent="dragActive = true"
      @dragleave.prevent="dragActive = false"
      @drop.prevent="onDrop">
      <input ref="fileInput" hidden type="file" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" @change="onFiles(($event.target as HTMLInputElement).files)">
      <strong>Excel 파일을 선택하세요.</strong>
      <span>.xlsx · 최대 {{ Math.round((definition.maxFileSizeBytes ?? 10485760) / 1048576) }}MB</span>
      <KbxButton label="파일 선택" variant="primary" :loading="busy" @click="chooseFile" />
      <small>Drag & Drop은 보조 기능이며 파일 선택 버튼은 항상 제공됩니다.</small>
      <p v-if="clientIssue" class="kbx-import__client-error" role="alert">{{clientIssue}}</p>
    </div>

    <section v-else-if="step === 2" class="kbx-import__mapping" data-kbx-surface="mapping">
      <div class="mapping-head"><span>Excel 열</span><span>시스템 필드</span><span>매핑 근거</span></div>
      <div v-for="(mapping, index) in mappings" :key="mapping.sourceColumn" class="mapping-row">
        <strong>{{ mapping.sourceColumn }}</strong>
        <select :value="mapping.targetField ?? ''" :aria-invalid="mapping.targetField ? mappings.filter(item=>item.targetField===mapping.targetField).length>1 : undefined" @change="setTarget(index, ($event.target as HTMLSelectElement).value)">
          <option value="">매핑하지 않음</option>
          <option v-for="field in importableFields" :key="field.key" :value="field.key">
            {{ field.label }}{{ field.required ? ' *' : '' }}
          </option>
        </select>
        <span class="mapping-reason">
          {{ mapping.source === 'ai' ? `AI 추천 ${Math.round((mapping.confidence ?? 0) * 100)}%` : mapping.reason ?? mapping.source }}
        </span>
      </div>
      <div v-if="mappingIssues.length" class="mapping-issues" role="alert">
        <strong>매핑을 확인하세요.</strong>
        <ul><li v-for="issue in mappingIssues" :key="`${issue.code}:${issue.fieldKey}`">{{issue.message}}</li></ul>
      </div>
      <div class="mapping-save">
        <label>다음에도 사용할 매핑 이름 <input v-model="mappingName" maxlength="80" placeholder="예: 쿠팡 주문양식"></label>
        <KbxButton label="매핑 저장" variant="secondary" :disabled="!mappingName.trim() || Boolean(mappingIssues.length)" @click="emit('saveNamedMapping', mappingName.trim(), mappings)" />
      </div>
      <div class="kbx-import__actions">
        <KbxButton label="취소" variant="secondary" @click="emit('cancel')" />
        <KbxButton label="검증 시작" variant="primary" :disabled="!canValidate" :loading="busy" @click="emit('validate')" />
      </div>
    </section>

    <section v-else-if="step === 3" class="kbx-import__validation" data-kbx-surface="validation">
      <KbxJobProgress v-if="session?.status === 'validating'" :progress="progress ?? null" />
      <template v-else>
        <div class="summary" aria-label="검증 결과">
          <div><span>전체</span><strong>{{ session?.totalRows.toLocaleString() }}</strong></div>
          <div><span>정상</span><strong>{{ session?.validRows.toLocaleString() }}</strong></div>
          <div><span>오류</span><strong>{{ session?.invalidRows.toLocaleString() }}</strong></div>
          <div><span>경고</span><strong>{{ session?.warningRows.toLocaleString() }}</strong></div>
        </div>
        <div v-if="session?.errors?.length" class="errors">
          <div class="errors__head"><span>행</span><span>필드</span><span>사유</span></div>
          <div v-for="error in session.errors.slice(0, 100)" :key="`${error.rowNumber}-${error.field}-${error.code}`" class="errors__row" :data-severity="error.severity">
            <span>{{ error.rowNumber }}</span><span>{{ error.sourceColumn ?? error.field ?? '-' }}</span><span>{{ error.message }}</span>
          </div>
          <small v-if="session.errors.length > 100">화면에는 처음 100건만 표시합니다. 전체 오류는 오류파일로 확인하세요.</small>
        </div>
        <div class="kbx-import__actions">
          <KbxButton v-if="session?.invalidRows" label="오류파일 다운로드" variant="secondary" @click="emit('downloadErrors')" />
          <KbxButton label="정상 데이터 반영" variant="primary" :disabled="!canCommit" @click="requestCommit" />
        </div>
      </template>
    </section>

    <section v-else class="kbx-import__commit" data-kbx-surface="result" :data-result="session?.status">
      <KbxJobProgress v-if="session?.status === 'committing'" :progress="progress ?? null" />
      <div v-else-if="isFailed" class="kbx-import__terminal is-error" role="alert">
        <strong>{{session?.failure?.title ?? '반영 작업을 완료하지 못했습니다.'}}</strong>
        <p>{{session?.failure?.detail ?? '원본 업로드와 작업 이력은 유지됩니다. 원인을 확인한 뒤 다시 진행하세요.'}}</p>
        <small v-if="session?.failure?.code">오류코드 {{session.failure.code}}</small>
        <div class="kbx-import__actions"><KbxButton label="새 파일로 다시 시작" variant="secondary" @click="emit('cancel')" /></div>
      </div>
      <div v-else-if="isCancelled" class="kbx-import__terminal">
        <strong>반영 작업이 취소되었습니다.</strong><p>새 파일을 선택해 다시 시작할 수 있습니다.</p>
        <div class="kbx-import__actions"><KbxButton label="새 파일 선택" variant="secondary" @click="emit('cancel')" /></div>
      </div>
      <template v-else>
        <div class="summary summary--result" :class="{'is-partial':isPartial}">
          <div><span>신규</span><strong>{{ session?.createdRows.toLocaleString() }}</strong></div>
          <div><span>수정</span><strong>{{ session?.updatedRows.toLocaleString() }}</strong></div>
          <div><span>오류</span><strong>{{ session?.invalidRows.toLocaleString() }}</strong></div>
        </div>
        <p v-if="isPartial" class="kbx-import__partial">정상 데이터는 반영되었고 오류 데이터는 제외되었습니다. 오류파일로 실패 건만 다시 처리할 수 있습니다.</p>
        <div v-if="isPartial && session?.invalidRows" class="kbx-import__actions"><KbxButton label="오류파일 다운로드" variant="secondary" @click="emit('downloadErrors')" /></div>
      </template>
    </section>

    <KbxConfirm
      :open="commitConfirmOpen"
      title="정상 데이터를 반영하시겠습니까?"
      :detail="`${(session?.validRows ?? 0).toLocaleString()}건을 반영합니다. 오류 ${(session?.invalidRows ?? 0).toLocaleString()}건은 제외됩니다.`"
      level="high"
      :confirm-label="`${(session?.validRows ?? 0).toLocaleString()}건 반영`"
      @update:open="commitConfirmOpen=$event"
      @confirm="confirmCommit"
    />
  </section>
</template>

<style scoped>
.kbx-import{display:grid;gap:var(--kbx-space-4);min-width:0}.kbx-import__header{display:flex;justify-content:space-between;gap:var(--kbx-space-4);align-items:flex-start}.kbx-import__header h2{margin:0 0 var(--kbx-space-1);font-size:var(--kbx-font-xl)}.kbx-import__header p{margin:0;color:var(--kbx-color-text-muted);font-size:var(--kbx-font-sm)}.kbx-import__drop{min-height:var(--kbx-import-drop-min-height);border:var(--kbx-border-width) dashed var(--kbx-color-border-strong);display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--kbx-space-2);background:var(--kbx-color-surface-muted)}.kbx-import__drop.is-dragging{outline:calc(var(--kbx-border-width) * 2) solid var(--kbx-color-focus);outline-offset:calc(var(--kbx-border-width) * -2)}.kbx-import__drop>span,.kbx-import__drop small{color:var(--kbx-color-text-muted)}.kbx-import__client-error{margin:0;color:var(--kbx-color-danger);font-size:var(--kbx-font-sm)}.kbx-import__mapping,.kbx-import__validation,.kbx-import__commit{border:var(--kbx-border-width) solid var(--kbx-color-border);background:var(--kbx-color-surface);padding:var(--kbx-space-3)}.mapping-head,.mapping-row{display:grid;grid-template-columns:minmax(var(--kbx-import-source-column-min-width),1fr) minmax(var(--kbx-import-target-column-min-width),1.3fr) minmax(var(--kbx-import-reason-column-min-width),.8fr);gap:var(--kbx-space-3);min-height:var(--kbx-control-md);align-items:center;border-bottom:var(--kbx-border-width) solid var(--kbx-color-border)}.mapping-head{font-size:var(--kbx-font-xs);font-weight:600;color:var(--kbx-color-text-muted)}.mapping-row select{height:var(--kbx-control-height);border:var(--kbx-border-width) solid var(--kbx-color-border-strong);border-radius:var(--kbx-radius-sm);background:var(--kbx-color-surface)}.mapping-row select[aria-invalid="true"]{border-color:var(--kbx-color-danger);background:var(--kbx-color-danger-surface)}.mapping-reason{font-size:var(--kbx-font-xs);color:var(--kbx-color-text-muted)}.mapping-issues{margin-top:var(--kbx-space-3);padding:var(--kbx-space-3);border:var(--kbx-border-width) solid var(--kbx-color-danger-border);background:var(--kbx-color-danger-surface);font-size:var(--kbx-font-sm)}.mapping-issues ul{margin:var(--kbx-space-2) 0 0;padding-left:var(--kbx-space-5)}.mapping-save{display:flex;align-items:center;justify-content:flex-end;gap:var(--kbx-space-2);padding-top:var(--kbx-space-3)}.mapping-save label{display:flex;align-items:center;gap:var(--kbx-space-2);font-size:var(--kbx-font-sm)}.mapping-save input{width:var(--kbx-import-mapping-name-width);height:var(--kbx-control-height);border:var(--kbx-border-width) solid var(--kbx-color-border-strong);border-radius:var(--kbx-radius-sm);padding:0 var(--kbx-space-2)}.summary{display:grid;grid-template-columns:repeat(4,minmax(var(--kbx-summary-item-min-width),1fr));border:var(--kbx-border-width) solid var(--kbx-color-border);margin-bottom:var(--kbx-space-3)}.summary>div{padding:var(--kbx-space-3);display:flex;justify-content:space-between;border-right:var(--kbx-border-width) solid var(--kbx-color-border)}.summary>div:last-child{border-right:0}.summary strong{font-size:var(--kbx-font-xl)}.summary--result{grid-template-columns:repeat(3,minmax(var(--kbx-summary-item-min-width),1fr))}.summary--result.is-partial{border-color:var(--kbx-color-warning-border)}.errors{border:var(--kbx-border-width) solid var(--kbx-color-border)}.errors__head,.errors__row{display:grid;grid-template-columns:var(--kbx-import-error-row-width) var(--kbx-import-error-field-width) 1fr;gap:var(--kbx-space-2);min-height:var(--kbx-control-height);align-items:center;padding:0 var(--kbx-space-2);border-bottom:var(--kbx-border-width) solid var(--kbx-color-border);font-size:var(--kbx-font-sm)}.errors__head{font-weight:600;background:var(--kbx-color-surface-muted)}.errors__row[data-severity="warning"]{background:var(--kbx-color-warning-surface)}.errors__row[data-severity="error"]{background:var(--kbx-color-danger-surface)}.errors__row:last-of-type{border-bottom:0}.errors small{display:block;padding:var(--kbx-space-2);color:var(--kbx-color-text-muted)}.kbx-import__terminal{display:grid;gap:var(--kbx-space-2);padding:var(--kbx-space-4);background:var(--kbx-color-surface-muted);border:var(--kbx-border-width) solid var(--kbx-color-border)}.kbx-import__terminal.is-error{background:var(--kbx-color-danger-surface);border-color:var(--kbx-color-danger-border)}.kbx-import__terminal p,.kbx-import__partial{margin:0;color:var(--kbx-color-text-muted);font-size:var(--kbx-font-sm)}.kbx-import__actions{display:flex;justify-content:flex-end;gap:var(--kbx-space-2);margin-top:var(--kbx-space-3)}
</style>
