<script setup>
import AppLayout from "@/Layouts/AppLayout.vue";
import TemplateControls from "./Partials/TemplateControls.vue";
import ChecklistSteps from "./Partials/ChecklistSteps.vue";
import MappingTable from "./Partials/MappingTable.vue";
import ActionsBar from "./Partials/ActionsBar.vue";
import SavedMappingsTable from "./Partials/SavedMappingsTable.vue";
import LogsTable from "./Partials/LogsTable.vue";
import ProcessResult from "./Partials/ProcessResult.vue";
import { ref, computed, onMounted, watch } from "vue";
import { router } from "@inertiajs/vue3";
import axios from "axios";
import Modal from "@/Components/Modal.vue";
import CsvPreviewModal from "./Partials/CsvPreviewModal.vue";
import SimulationModal from "./Partials/SimulationModal.vue";
import MissingContractsModal from "./Partials/MissingContractsModal.vue";
import FoundContractsModal from "./Partials/FoundContractsModal.vue";
import UnresolvedRowsModal from "./Partials/UnresolvedRowsModal.vue";
import { useCurrencyFormat } from "./useCurrencyFormat.js";
import { Switch } from "@/Components/ui/switch";
import { Label } from "@/Components/ui/label";
import { Button } from "@/Components/ui/button";
import { Badge } from "@/Components/ui/badge";
import { Checkbox } from "@/Components/ui/checkbox";

// Reintroduce props definition lost during earlier edits
const props = defineProps({
  import: Object,
  templates: Array,
  clients: Array,
  client: Object,
});

// Core reactive state (restored)
const importId = ref(props.import?.id || null);
const hasHeader = ref(Boolean(props.import?.meta?.has_header ?? true));
const detected = ref({
  columns: props.import?.meta?.columns || [],
  delimiter: props.import?.meta?.detected_delimiter || ",",
  has_header: hasHeader.value,
});
const templateApplied = ref(false);
const processing = ref(false);
const processResult = ref(null);
const mappingRows = ref([]);
const mappingSaved = ref(false);
const mappingSavedCount = ref(0);
const mappingError = ref("");
const savingMappings = ref(false);
const persistedMappings = ref([]); // raw persisted
let suppressMappingWatch = false; // guard to avoid resetting saved flag on programmatic updates
const persistedSignature = ref(""); // signature of last persisted mapping set
// (Reverted) We no longer fetch template-specific source columns; coverage uses detected columns
const detectedNote = ref("");
const delimiterState = ref({ mode: "auto", custom: "" });
const effectiveDelimiter = computed(() => {
  switch (delimiterState.value.mode) {
    case "auto":
      return null;
    case "comma":
      return ",";
    case "semicolon":
      return ";";
    case "tab":
      return "\t";
    case "pipe":
      return "|";
    case "space":
      return " ";
    case "custom":
      return delimiterState.value.custom || null;
    default:
      return null;
  }
});
const initForced = props.import?.meta?.forced_delimiter || null;
if (initForced) {
  const map = { ",": "comma", ";": "semicolon", "\t": "tab", "|": "pipe", " ": "space" };
  const mode = map[initForced] || "custom";
  delimiterState.value.mode = mode;
  if (mode === "custom") delimiterState.value.custom = initForced;
}
const events = ref([]);
const eventsLimit = ref(200);
const loadingEvents = ref(false);
const showPreview = ref(false);
const previewLoading = ref(false);
const previewRows = ref([]);
const previewColumns = ref([]);
const previewTruncated = ref(false);
const previewLimit = ref(200);

// Import options (persisted on Import): show_missing and reactivate
const showMissingEnabled = ref(Boolean(props.import?.show_missing ?? false));
const reactivateEnabled = ref(Boolean(props.import?.reactivate ?? false));

async function saveImportOptions() {
  if (!importId.value) return;
  try {
    await axios.post(
      route("imports.options", { import: importId.value }),
      {
        show_missing: !!showMissingEnabled.value,
        // keep existing reactivate value if UI doesn't expose it here
        reactivate: !!reactivateEnabled.value,
      },
      { headers: { Accept: "application/json" }, withCredentials: true }
    );
  } catch (e) {
    console.error(
      "Save import options failed",
      e.response?.status || "",
      e.response?.data || e
    );
  }
}

// Missing contracts (post-finish) UI state
const showMissingContracts = ref(false);
const missingContractsLoading = ref(false);
const missingContracts = ref([]);
const contractRefIsKeyref = computed(() => {
  return (persistedMappings.value || []).some((m) => {
    const tf = String(m?.target_field || "")
      .toLowerCase()
      .trim();
    const am = String(m?.apply_mode || "")
      .toLowerCase()
      .trim();
    return ["contract.reference", "contracts.reference"].includes(tf) && am === "keyref";
  });
});
const canShowMissingButton = computed(() => {
  return contractRefIsKeyref.value && !!showMissingEnabled.value;
});
async function openMissingContracts() {
  if (!importId.value || !contractRefIsKeyref.value) return;
  showMissingContracts.value = true;
  missingContractsLoading.value = true;
  try {
    const { data } = await axios.get(
      route("imports.missing-contracts", { import: importId.value }),
      {
        headers: { Accept: "application/json" },
        withCredentials: true,
      }
    );
    missingContracts.value = Array.isArray(data?.missing) ? data.missing : [];
  } catch (e) {
    console.error(
      "Missing contracts fetch failed",
      e.response?.status || "",
      e.response?.data || e
    );
    missingContracts.value = [];
  } finally {
    missingContractsLoading.value = false;
  }
}

// Unresolved keyref rows (contracts not found) UI state
const showUnresolved = ref(false);
const unresolvedLoading = ref(false);
const unresolvedColumns = ref([]);
const unresolvedRows = ref([]); // [{id,row_number,values:[]}]
async function openUnresolved() {
  if (!importId.value || !contractRefIsKeyref.value) return;
  showUnresolved.value = true;
  unresolvedLoading.value = true;
  try {
    const { data } = await axios.get(
      route("imports.missing-keyref-rows", { import: importId.value }),
      { headers: { Accept: "application/json" }, withCredentials: true }
    );
    unresolvedColumns.value = Array.isArray(data?.columns) ? data.columns : [];
    unresolvedRows.value = Array.isArray(data?.rows) ? data.rows : [];
  } catch (e) {
    console.error(
      "Unresolved keyref rows fetch failed",
      e.response?.status || "",
      e.response?.data || e
    );
    unresolvedColumns.value = [];
    unresolvedRows.value = [];
  } finally {
    unresolvedLoading.value = false;
  }
}

// History import: list of contracts that already existed in DB and were matched
const isHistoryImport = computed(() => {
  const foundList = props.import?.meta?.history_found_contracts;
  const hasFound = Array.isArray(foundList) && foundList.length > 0;
  return Boolean(
    props.import?.template?.meta?.history_import ??
      props.import?.import_template?.meta?.history_import ??
      props.import?.meta?.history_import ??
      hasFound
  );
});
const historyFoundContracts = computed(() => {
  const list = props.import?.meta?.history_found_contracts;
  return Array.isArray(list) ? list : [];
});
const showFoundContracts = ref(false);

// Determine if all detected columns are mapped with entity+field
function evaluateMappingSaved() {
  console.log("here the evaluation happen of mapping save!");
  const hasTemplate =
    !!props.import?.import_template_id || !!form.value.import_template_id;
  if (!hasTemplate) return;
  // We only require coverage of template-defined source columns when a template is present.
  // Template source columns are derived from persistedMappings (these reflect the template's mapping set)
  // NOT every detected column (there may be extra columns in the uploaded file that the template intentionally ignores).
  const detectedColsNorm = Array.isArray(detected.value.columns)
    ? detected.value.columns.map((c) => normalizeSource(c)).filter(Boolean)
    : [];

  // Determine required source columns:
  //  - If we have any persisted mappings (template applied or saved), use their source columns as the required set.
  //  - Otherwise (edge case: template id present but no persisted mappings yet), fall back to detected columns.
  const templateSourceCols = Array.from(
    new Set(
      persistedMappings.value.map((m) => normalizeSource(m.source_column)).filter(Boolean)
    )
  );
  const requiredSources = templateSourceCols.length
    ? templateSourceCols
    : detectedColsNorm;
  if (!requiredSources.length) return;

  // A source column is considered covered if there exists a persisted mapping for it.
  const mappedSources = new Set(
    persistedMappings.value.map((m) => normalizeSource(m.source_column)).filter(Boolean)
  );
  if (!requiredSources.every((c) => mappedSources.has(c))) return; // incomplete coverage

  // Now ensure that every required source column has an entity+field selected (unless the row is explicitly skipped).
  const allHaveTargets = mappingRows.value.every((r) => {
    const src = normalizeSource(r.source_column || "");
    if (!src || !requiredSources.includes(src)) return true; // ignore non-required / extra columns
    if (r.skip) return true; // skipped rows do not block completion
    return !!(r.entity && r.field);
  });
  if (!allHaveTargets) return;

  mappingSaved.value = true;
  mappingSavedCount.value = mappingRows.value.filter(
    (r) => r.entity && r.field && !r.skip
  ).length;
  persistedSignature.value = computeMappingSignature(mappingRows.value);
}

function normalizeOptions(val) {
  if (!val) {
    return {};
  }
  if (typeof val === "string") {
    try {
      const parsed = JSON.parse(val);
      return parsed && typeof parsed === "object" ? parsed : {};
    } catch (e) {
      return {};
    }
  }
  if (typeof val === "object") {
    return val;
  }
  return {};
}

function computeMappingSignature(rows) {
  return rows
    .filter((r) => r && r.source_column)
    .map((r) => {
      const src = normalizeSource(r.source_column || "");
      const tgt = r.entity && r.field ? `${entityKeyToRecord(r.entity)}.${r.field}` : "";
      return `${src}=>${tgt}`;
    })
    .sort()
    .join("|");
}

// Entity definitions & state (restored)
const entityDefs = ref([]); // [{ key, label, canonical_root, fields: [] }]
const usingEntityFallback = ref(false);

// Completion & gating
const isCompleted = computed(() => (props.import?.status || "") === "completed");
const hasPersistedMappings = computed(() => (persistedMappings.value?.length || 0) > 0);
const canProcess = computed(
  () =>
    !!importId.value &&
    !processing.value &&
    hasPersistedMappings.value &&
    !isCompleted.value
);

// Preview helpers
async function openPreview() {
  if (!importId.value) return;
  showPreview.value = true;
  await fetchPreview();
}
async function fetchPreview() {
  if (!importId.value) return;
  previewLoading.value = true;
  try {
    const { data } = await axios.get(
      route("imports.preview", { import: importId.value }),
      {
        params: { limit: previewLimit.value },
        headers: { Accept: "application/json" },
        withCredentials: true,
      }
    );
    previewColumns.value = Array.isArray(data?.columns) ? data.columns : [];
    previewRows.value = Array.isArray(data?.rows) ? data.rows : [];
    previewTruncated.value = !!data?.truncated;
  } catch (e) {
    console.error(
      "Preview fetch failed",
      e.response?.status || "",
      e.response?.data || e
    );
  } finally {
    previewLoading.value = false;
  }
}
const entityOptions = computed(() =>
  entityDefs.value.map((e) => ({ value: e.key, label: e.label || e.key }))
);
const fieldOptionsByEntity = computed(() =>
  Object.fromEntries(
    entityDefs.value.map((e) => [e.key, Array.isArray(e.fields) ? e.fields : []])
  )
);
const canonicalRootByKey = computed(() =>
  Object.fromEntries(entityDefs.value.map((e) => [e.key, e.canonical_root || e.key]))
);
const keyByCanonicalRoot = computed(() => {
  const m = {};
  for (const e of entityDefs.value) {
    if (e.canonical_root) {
      m[e.canonical_root] = e.key;
    }
  }
  return m;
});

// Provide fields for either a UI key (e.g. "contracts") or a canonical root (e.g. "contract")
function fieldsForEntity(entityOrCanonical) {
  if (!entityOrCanonical) return [];
  // Try direct key first
  const direct = fieldOptionsByEntity.value[entityOrCanonical];
  if (Array.isArray(direct)) return direct;
  // If a canonical root was stored before entityDefs loaded, map it now
  const byKey = keyByCanonicalRoot.value[entityOrCanonical];
  if (byKey) {
    const via = fieldOptionsByEntity.value[byKey];
    if (Array.isArray(via)) return via;
  }
  return [];
}

function defaultEntityDefs() {
  return [
    {
      key: "person",
      label: "Person",
      canonical_root: "person",
      fields: [
        "first_name",
        "last_name",
        "full_name",
        "gender",
        "birthday",
        "tax_number",
        "social_security_number",
        "description",
      ],
    },
    {
      key: "person_addresses",
      label: "Person Addresses",
      canonical_root: "address",
      fields: ["address", "country", "type_id", "description"],
    },
    {
      key: "person_phones",
      label: "Person Phones",
      canonical_root: "phone",
      fields: ["nu", "country_code", "type_id", "description"],
    },
    {
      key: "emails",
      label: "Emails",
      canonical_root: "email",
      fields: ["value", "is_primary", "label"],
    },
    {
      key: "contracts",
      label: "Contracts",
      canonical_root: "contract",
      fields: [
        "reference",
        "start_date",
        "end_date",
        "description",
        "type_id",
        "client_case_id",
        "meta",
      ],
    },
    {
      key: "accounts",
      label: "Accounts",
      canonical_root: "account",
      fields: [
        "reference",
        "balance_amount",
        "contract_id",
        "contract_reference",
        "type_id",
        "active",
        "description",
      ],
    },
  ];
}

async function loadEntityDefs() {
  try {
    const { data } = await axios.get("/api/import-entities");
    const items = Array.isArray(data?.entities) ? data.entities : [];
    // Normalize fields to arrays and ensure labels
    const normalized = items.map((e) => ({
      key: e.key,
      label: e.label || e.key,
      canonical_root: e.canonical_root || e.key,
      fields: Array.isArray(e.fields) ? e.fields : [],
    }));
    if (!normalized.length) {
      usingEntityFallback.value = true;
      entityDefs.value = defaultEntityDefs();
    } else {
      usingEntityFallback.value = false;
      entityDefs.value = normalized;
    }
    // Normalize any existing mapping row entity values to UI keys if they are canonical roots
    if (Array.isArray(mappingRows.value) && mappingRows.value.length) {
      suppressMappingWatch = true;
      const mapCanonToKey = keyByCanonicalRoot.value;
      mappingRows.value = mappingRows.value.map((r) => {
        const current = r.entity;
        if (current && !entityDefs.value.find((e) => e.key === current)) {
          const maybeKey = mapCanonToKey[current];
          if (maybeKey) {
            return { ...r, entity: maybeKey };
          }
        }
        return r;
      });
      suppressMappingWatch = false;
    }
  } catch (e) {
    console.error("Failed to load import entity definitions", e);
    usingEntityFallback.value = true;
    entityDefs.value = defaultEntityDefs();
    // Also normalize with fallback
    if (Array.isArray(mappingRows.value) && mappingRows.value.length) {
      suppressMappingWatch = true;
      const mapCanonToKey = keyByCanonicalRoot.value;
      mappingRows.value = mappingRows.value.map((r) => {
        const current = r.entity;
        if (current && !entityDefs.value.find((e) => e.key === current)) {
          const maybeKey = mapCanonToKey[current];
          if (maybeKey) {
            return { ...r, entity: maybeKey };
          }
        }
        return r;
      });
      suppressMappingWatch = false;
    }
  }
}

// Display rows used by the table: prefer mappingRows if present; otherwise fall back to detected columns
const displayRows = computed(() => {
  if (Array.isArray(mappingRows.value) && mappingRows.value.length > 0) {
    return mappingRows.value;
  }
  const cols = detected.value?.columns || [];
  return cols.map((c, idx) => ({
    source_column: c,
    entity: "",
    field: "",
    skip: false,
    transform: "trim",
    apply_mode: "both",
    options: {},
    position: idx,
  }));
});

// Header normalization and guess mapping for auto-assigning sensible defaults
function stripDiacritics(s) {
  if (!s) return "";
  return String(s)
    .replace(/[čć]/gi, "c")
    .replace(/[š]/gi, "s")
    .replace(/[ž]/gi, "z")
    .replace(/[đ]/gi, "d")
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "");
}
function normalizeHeader(h) {
  if (!h) return "";
  const base = stripDiacritics(String(h)).toLowerCase();
  return base
    .replace(/[^a-z0-9]+/g, " ")
    .trim()
    .replace(/\s+/g, "");
}

// Frontend auto-guessing disabled: entity/field will remain blank unless loaded from persisted mappings or set by the user
function guessMappingForHeader(h) {
  return null;
}

// Normalize source column to match persisted mappings in a case/space/diacritic-insensitive way
function normalizeSource(s) {
  return normalizeHeader(s);
}

// Entity and field options are provided dynamically by API via entityOptions and fieldOptionsByEntity

// Local state for selects
const form = ref({
  client_uuid: props.client.uuid,
  import_template_id: props.import?.import_template_id || null,
});

// Initialize client_uuid from import.client_uuid (preferred) using provided clients list
if (props.import?.client_uuid) {
  const found = (props.clients || []).find((c) => c.uuid === props.import.client_uuid);
  form.value.client_uuid = found ? found.uuid : null;
}

const selectedClientOption = computed({
  get() {
    const cuuid = form.value.client_uuid;
    if (!cuuid) return null;
    return (props.clients || []).find((c) => c.uuid === cuuid) || null;
  },
  set(val) {
    form.value.client_uuid = val ? val.uuid : null;
  },
});

const selectedTemplateOption = computed({
  get() {
    const tid = form.value.import_template_id;
    if (tid == null) return null;
    return (props.templates || []).find((t) => t.id === tid) || null;
  },
  set(val) {
    form.value.import_template_id = val ? val.id : null;
  },
});

// Show only global templates when no client is selected.
// When a client is selected, show only that client's templates (strict match by client_uuid, no globals).
const filteredTemplates = computed(() => {
  const cuuid = form.value.client_uuid;
  const list = props.templates || [];
  if (!cuuid) {
    return list.filter((t) => t.client_id == null);
  }
  return list.filter((t) => t.client_uuid && t.client_uuid === cuuid);
});

const selectedMappingsCount = computed(
  () => mappingRows.value.filter((r) => !r.skip && r.entity && r.field).length
);

// --- UI Enhancements: Status badge, inline validation, checklist ---
const statusInfo = computed(() => {
  const raw = (props.import?.status || "").toLowerCase();
  const map = {
    completed: {
      label: "Zaključeno",
      classes: "bg-emerald-100 text-emerald-700 border border-emerald-300",
      variant: "default",
    },
    processing: {
      label: "Obdelava",
      classes: "bg-indigo-100 text-indigo-700 border border-indigo-300",
      variant: "default",
    },
    validating: {
      label: "Preverjanje",
      classes: "bg-indigo-100 text-indigo-700 border border-indigo-300",
      variant: "default",
    },
    failed: {
      label: "Neuspešno",
      classes: "bg-red-100 text-red-700 border border-red-300",
      variant: "destructive",
    },
    parsed: {
      label: "Razčlenjeno",
      classes: "bg-slate-100 text-slate-700 border border-slate-300",
      variant: "secondary",
    },
    uploaded: {
      label: "Naloženo",
      classes: "bg-slate-100 text-slate-700 border border-slate-300",
      variant: "secondary",
    },
  };
  return (
    map[raw] || {
      label: raw || "Status",
      classes: "bg-gray-100 text-gray-700 border border-gray-300",
      variant: "outline",
    }
  );
});

// Duplicate target (entity+field) detection for inline validation
const duplicateTargets = computed(() => {
  const counts = new Map();
  for (const r of mappingRows.value) {
    if (!r.skip && r.entity && r.field) {
      const key = entityKeyToRecord(r.entity) + "." + r.field;
      counts.set(key, (counts.get(key) || 0) + 1);
    }
  }
  const dups = new Set();
  counts.forEach((v, k) => {
    if (v > 1) dups.add(k);
  });
  return dups;
});
function duplicateTarget(row) {
  if (!row || !row.entity || !row.field) return false;
  const key = entityKeyToRecord(row.entity) + "." + row.field;
  return duplicateTargets.value.has(key);
}

// Critical fields heuristic (extend as needed)
const criticalFields = computed(() => {
  const base = ["contract.reference"];
  const paymentsImport = !!selectedTemplateOption.value?.meta?.payments_import;
  if (paymentsImport) {
    base.push("payment.amount", "payment.payment_date");
  }
  return base;
});
const providedTargets = computed(() => {
  const set = new Set();
  for (const r of mappingRows.value) {
    if (!r.skip && r.entity && r.field) {
      set.add(entityKeyToRecord(r.entity) + "." + r.field);
    }
  }
  return set;
});
const missingCritical = computed(() =>
  criticalFields.value.filter((f) => !providedTargets.value.has(f))
);

// Checklist steps
const stepStates = computed(() => [
  { label: "1) Izberi predlogo", done: !!form.value.import_template_id },
  { label: "2) Preglej stolpce", done: (detected.value.columns || []).length > 0 },
  { label: "3) Preslikaj", done: selectedMappingsCount.value > 0 },
  { label: "4) Shrani", done: mappingSaved.value },
  { label: "5) Obdelaj", done: isCompleted.value || !!processResult.value },
]);

async function fetchColumns() {
  if (!importId.value) return;
  const url = route("imports.columns", { import: importId.value });
  const params = { has_header: hasHeader.value ? 1 : 0 };
  if (effectiveDelimiter.value) {
    params.delimiter = effectiveDelimiter.value;
  }
  const { data } = await axios.get(url, { params });
  // Normalize columns to strings for consistent rendering
  const colsRaw = Array.isArray(data.columns) ? data.columns : [];
  const normCols = colsRaw
    .map((c) => {
      if (typeof c === "string" || typeof c === "number") return String(c);
      if (c && typeof c === "object") {
        return String(c.name ?? c.header ?? c.label ?? Object.values(c)[0] ?? "");
      }
      return "";
    })
    .filter(Boolean);
  detected.value = {
    columns: normCols,
    delimiter: data.detected_delimiter || ",",
    has_header: !!data.has_header,
  };

  console.table(colsRaw, normCols);
  detectedNote.value = data.note || "";
  // initialize mapping rows if empty
  if (!mappingRows.value.length && detected.value.columns.length) {
    suppressMappingWatch = true;
    mappingRows.value = detected.value.columns.map((c, idx) => ({
      source_column: c,
      entity: "",
      field: "",
      skip: false,
      transform: "trim",
      apply_mode: "both",
      options: {},
      position: idx,
    }));
    suppressMappingWatch = false;
    evaluateMappingSaved();
  }
  await loadImportMappings();
  // Fallback: if no detected columns were found, but persisted mappings exist, use them to render the grid
  if (
    (!detected.value.columns || detected.value.columns.length === 0) &&
    mappingRows.value.length === 0 &&
    persistedMappings.value.length > 0
  ) {
    mappingRows.value = persistedMappings.value.map((m, idx) => {
      const tf = String(m.target_field || "");
      const [record, field] = tf ? tf.split(".", 2) : ["", ""];
      return {
        source_column: m.source_column,
        entity: recordToEntityKey(record),
        field: field || "",
        skip: false,
        transform: m.transform || "trim",
        apply_mode: m.apply_mode || "both",
        options: normalizeOptions(m.options),
        position: idx,
      };
    });
  }
}

async function applyTemplateToImport() {
  if (!importId.value || !form.value.import_template_id) return;

  // Find the selected template to get its UUID
  const template = (props.templates || []).find(
    (t) => t.id === form.value.import_template_id
  );
  if (!template?.uuid) {
    console.error("Template UUID not found");
    return;
  }

  try {
    if (templateApplied.value) {
      const ok = window.confirm(
        "Re-apply this template? This will overwrite current mappings for this import."
      );
      if (!ok) {
        return;
      }
    }
    await axios.post(
      route("importTemplates.apply", {
        template: template.uuid,
        import: importId.value,
      }),
      {},
      {
        headers: { Accept: "application/json" },
        withCredentials: true,
      }
    );
    templateApplied.value = true;
    // If template has a default delimiter, adopt it and refetch columns
    const tpl = selectedTemplateOption.value;
    const tplDelim = tpl?.delimiter || tpl?.meta?.delimiter || null;
    if (tplDelim) {
      // map to known mode if possible, else set custom
      const map = {
        ",": "comma",
        ";": "semicolon",
        "\t": "tab",
        "|": "pipe",
        " ": "space",
      };
      const mode = map[tplDelim] || "custom";
      delimiterState.value.mode = mode;
      if (mode === "custom") delimiterState.value.custom = tplDelim;
      await fetchColumns();
    }
    await loadImportMappings();
  } catch (e) {
    templateApplied.value = false;
    console.error(
      "Apply template error",
      e.response?.status || "",
      e.response?.data || e
    );
  }
}

async function loadImportMappings() {
  if (!importId.value) return;
  try {
    const { data } = await axios.get(
      route("imports.mappings.get", { import: importId.value }),
      {
        headers: { Accept: "application/json" },
        withCredentials: true,
      }
    );
    console.log(data);
    const rows = Array.isArray(data?.mappings) ? data.mappings : [];

    // Store raw persisted mappings for display regardless of detected columns
    persistedMappings.value = rows.slice();
    if (!rows.length) return;
    const bySource = new Map(rows.map((r) => [normalizeSource(r.source_column), r]));
    suppressMappingWatch = true;
    mappingRows.value = (mappingRows.value || []).map((r, idx) => {
      const m = bySource.get(normalizeSource(r.source_column));
      if (!m) return r;
      const tf = String(m.target_field || "");
      let entity = m.entity || "";
      let field = r.field || "";
      if (tf) {
        const [record, fld] = tf.split(".", 2);
        const inferred = recordToEntityKey(record);
        if (!entity) entity = inferred;
        if (fld) field = fld;
      }
      return {
        ...r,
        entity,
        field,
        transform: m.transform || "",
        apply_mode: m.apply_mode || "both",
        options: normalizeOptions(m.options) || r.options || {},
        skip: false,
        position: idx,
      };
    });

    // Auto-evaluate mappingSaved when a template is already bound to the import.
    // Previous logic required ALL detected columns. Updated: if a template is bound, only require template (persisted) source columns.
    if (props.import?.import_template_id) {
      const templateSources = Array.from(
        new Set(
          persistedMappings.value
            .map((m) => normalizeSource(m.source_column))
            .filter(Boolean)
        )
      );
      if (templateSources.length) {
        const allHaveTargets = mappingRows.value.every((r) => {
          const src = normalizeSource(r.source_column || "");
          if (!src || !templateSources.includes(src)) return true; // ignore extras
          if (r.skip) return true;
          return !!(r.entity && r.field);
        });
        if (allHaveTargets) {
          mappingSaved.value = true;
          mappingSavedCount.value = mappingRows.value.filter(
            (r) => r.entity && r.field && !r.skip
          ).length;
        }
      }
    }
    suppressMappingWatch = false;
    evaluateMappingSaved();
  } catch (e) {
    console.error(
      "Load import mappings error",
      e.response?.status || "",
      e.response?.data || e
    );
    suppressMappingWatch = false;
  }
}

async function saveMappings() {
  if (!importId.value) return;
  mappingError.value = "";
  const mappings = mappingRows.value
    .filter((r) => !r.skip && r.entity && r.field)
    .map((r) => ({
      source_column: r.source_column,
      entity: r.entity || null,
      target_field: `${entityKeyToRecord(r.entity)}.${r.field}`,
      transform: r.transform || null,
      apply_mode: r.apply_mode || "both",
      options:
        r.field === "meta"
          ? {
              key: r.options?.key ?? null,
              type: r.options?.type ?? null,
            }
          : null,
    }));
  if (!mappings.length) {
    mappingSaved.value = false;
    mappingError.value =
      "Select entity and field for at least one column (or uncheck Skip) before saving.";
    return;
  }
  try {
    savingMappings.value = true;
    const url = route("imports.mappings.save", { import: importId.value });
    const { data } = await axios.post(
      url,
      { mappings },
      {
        headers: { Accept: "application/json" },
        withCredentials: true,
      }
    );
    mappingSaved.value = true;
    mappingSavedCount.value = Number(data?.saved || mappings.length);
    mappingError.value = "";
    // Refresh persisted mappings so Process gating reflects the actual DB state
    await loadImportMappings();
  } catch (e) {
    mappingSaved.value = false;
    console.error("Save mappings error", e.response?.status || "", e.response?.data || e);
  } finally {
    savingMappings.value = false;
  }
}

async function processImport() {
  if (!importId.value) return;
  processing.value = true;
  processResult.value = null;
  try {
    const { data } = await axios.post(
      route("imports.process", { import: importId.value }),
      {},
      {
        headers: { Accept: "application/json" },
        withCredentials: true,
      }
    );
    processResult.value = data;
    // Immediately refresh the page props to reflect the completed state
    // Reload only the 'import' prop to minimize payload; don't preserve state so UI reflects new status
    router.reload({ only: ["import"], preserveScroll: true, preserveState: false });
  } catch (e) {
    console.error(
      "Process import error",
      e.response?.status || "",
      e.response?.data || e
    );
    processResult.value = { error: "Processing failed" };
  } finally {
    processing.value = false;
  }
}

// Helpers using canonical roots from API
function entityKeyToRecord(key) {
  return canonicalRootByKey.value[key] || key;
}
function recordToEntityKey(record) {
  return keyByCanonicalRoot.value[record] || record;
}

// Initial load
onMounted(async () => {
  await loadEntityDefs();
  // Build mapping grid from existing meta columns if present
  if (detected.value.columns?.length) {
    mappingRows.value = (detected.value.columns || [])
      // keep only defined, non-empty values (after trimming)
      .filter((c) => c !== undefined && c !== null && String(c).trim() !== "")
      .map((c, idx) => {
        return {
          source_column: c,
          entity: "",
          field: "",
          skip: false,
          transform: "trim",
          apply_mode: "both",
          options: {},
          position: idx,
        };
      });

    await loadImportMappings();
    console.log(mappingRows.value);
  } else {
    await fetchColumns();
  }
  // Auto-apply template mapping once if a template is selected and not already applied
  console.log(templateApplied.value);
  try {
    if (!templateApplied.value && form.value.import_template_id && importId.value) {
      await applyTemplateToImport();
    }
  } catch (e) {
    console.warn("Auto apply template failed", e);
  }
  // Load recent events (logs)
  await fetchEvents();
  // If template already bound when opening page, load template mapping columns
});

// Detect user changes (vs programmatic) using signature diff
watch(
  mappingRows,
  () => {
    if (suppressMappingWatch) return;
    const currentSig = computeMappingSignature(mappingRows.value);
    if (persistedSignature.value && currentSig === persistedSignature.value) {
      // No semantic change compared to persisted state
      return;
    }
    // Real change -> unsaved
    mappingSaved.value = false;
    mappingSavedCount.value = mappingRows.value.filter(
      (r) => r.entity && r.field && !r.skip
    ).length;
    mappingError.value = "";
  },
  { deep: true }
);

// If detected columns are loaded after mount, initialize mapping rows once
watch(
  () => detected.value.columns,
  (cols) => {
    if (Array.isArray(cols) && cols.length > 0 && mappingRows.value.length === 0) {
      suppressMappingWatch = true;
      mappingRows.value = cols.map((c, idx) => {
        return {
          source_column: c,
          entity: "",
          field: "",
          skip: false,
          transform: "trim",
          apply_mode: "both",
          options: {},
          position: idx,
        };
      });
      suppressMappingWatch = false;
      evaluateMappingSaved();
    }
  }
);

// If user changes delimiter selection, refresh detected columns
watch(
  () => delimiterState.value,
  async () => {
    if (importId.value) {
      await fetchColumns();
    }
  },
  { deep: true }
);

async function fetchEvents() {
  if (!importId.value) return;
  loadingEvents.value = true;
  try {
    const { data } = await axios.get(
      route("imports.events", { import: importId.value }),
      {
        params: { limit: eventsLimit.value },
        headers: { Accept: "application/json" },
        withCredentials: true,
      }
    );
    events.value = Array.isArray(data?.events) ? data.events : [];
  } catch (e) {
    console.error(
      "Load import events error",
      e.response?.status || "",
      e.response?.data || e
    );
  } finally {
    loadingEvents.value = false;
  }
}

async function downloadImport() {
  if (!importId.value) return;
  try {
    const url = route("imports.download", { import: importId.value });
    window.location.href = url;
  } catch (e) {
    console.error("Download failed", e);
  }
}

// Simulation (generic or payments) state
const showPaymentSim = ref(false);
const paymentSimLoading = ref(false);
const paymentSimLimit = ref(100);
const paymentSimRows = ref([]);
// summary (raw machine) + localized (povzetki.payment or others)
const paymentSimSummary = ref(null); // machine summary (if needed)
const paymentSimSummarySl = ref(null); // localized Slovenian summary
const paymentSimEntities = ref([]);
const paymentSimVerbose = ref(false); // "Podrobni pogled" toggle
const paymentsImport = computed(
  () => !!selectedTemplateOption.value?.meta?.payments_import
);

// Currency formatter with fallback (client currency -> EUR)
const clientCurrency = props.client?.currency || "EUR";
const { formatMoney } = useCurrencyFormat({
  primary: clientCurrency,
  fallbacks: ["EUR"],
});

async function openSimulation() {
  if (!importId.value) return;
  showPaymentSim.value = true;
  await fetchSimulation();
}
async function fetchSimulation() {
  if (!importId.value) return;
  paymentSimLoading.value = true;
  try {
    const routeName = paymentsImport.value
      ? "imports.simulatePayments" // legacy payments specific name
      : "imports.simulate"; // new generic simulation
    const { data } = await axios.get(route(routeName, { import: importId.value }), {
      params: {
        limit: paymentSimLimit.value,
        verbose: paymentSimVerbose.value ? 1 : 0,
      },
      headers: { Accept: "application/json" },
      withCredentials: true,
    });

    // V2 format
    paymentSimRows.value = Array.isArray(data?.rows) ? data.rows : [];
    paymentSimSummary.value = data?.summaries || null;

    // Extract unique entity types from rows for SimulationModal
    const entitySet = new Set();
    for (const row of data?.rows || []) {
      if (row.entities && typeof row.entities === "object") {
        Object.keys(row.entities).forEach((key) => entitySet.add(key));
      }
    }
    paymentSimEntities.value = Array.from(entitySet);
  } catch (e) {
    console.error("Simulation failed", e.response?.status || "", e.response?.data || e);
  } finally {
    paymentSimLoading.value = false;
  }
}
</script>

<template>
  <AppLayout :title="`Import ${props.import?.uuid || ''}`">
    <template #header>
      <div class="flex flex-col md:flex-row md:items-center md:justify-between gap-1">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">Nadaljuj uvoz</h2>
        <div class="text-sm text-gray-600 flex flex-wrap items-center gap-2">
          <span class="mr-2"
            >Stranka:
            <strong>{{
              selectedClientOption?.name || selectedClientOption?.uuid || "—"
            }}</strong>
          </span>
          <Badge v-if="templateApplied" variant="secondary" class="text-[10px]"
            >uporabljena</Badge
          >
          <Badge
            v-if="props.import?.status"
            :variant="statusInfo.variant || 'default'"
            class="text-xs"
            >{{ statusInfo.label }}</Badge
          >
          <Badge
            v-if="showMissingEnabled"
            variant="outline"
            class="text-[10px] bg-amber-50 text-amber-700 border-amber-200"
            >seznam manjkajočih</Badge
          >
        </div>
      </div>
    </template>
    <div class="py-6">
      <div class="max-w-5xl mx-auto sm:px-6 lg:px-8">
        <div class="bg-white shadow sm:rounded-lg p-6 space-y-6">
          <div
            v-if="isHistoryImport || historyFoundContracts.length"
            class="flex flex-wrap items-center gap-2 text-sm"
          >
            <Button
              variant="default"
              size="sm"
              class="bg-emerald-700 hover:bg-emerald-800 text-xs"
              @click.prevent="showFoundContracts = true"
              title="Prikaži pogodbe, ki so bile najdene in že obstajajo v bazi"
            >
              Najdene pogodbe
            </Button>
            <span v-if="historyFoundContracts.length" class="text-xs text-gray-600">
              {{ historyFoundContracts.length }} že obstoječih
            </span>
          </div>
          <div v-if="isCompleted" class="p-3 border rounded bg-gray-50 text-sm">
            <div class="flex flex-wrap gap-x-6 gap-y-1">
              <div>
                <span class="text-gray-600">Status:</span>
                <span class="font-medium text-emerald-700">Completed</span>
              </div>
              <div>
                <span class="text-gray-600">Finished:</span>
                <span class="font-medium">{{
                  props.import?.finished_at
                    ? new Date(props.import.finished_at).toLocaleString()
                    : "—"
                }}</span>
              </div>
              <div>
                <span class="text-gray-600">Total:</span>
                <span class="font-medium">{{ props.import?.total_rows ?? "—" }}</span>
              </div>
              <div>
                <span class="text-gray-600">Imported:</span>
                <span class="font-medium">{{ props.import?.imported_rows ?? "—" }}</span>
              </div>
              <div>
                <span class="text-gray-600">Invalid:</span>
                <span class="font-medium">{{ props.import?.invalid_rows ?? "—" }}</span>
              </div>
              <div>
                <span class="text-gray-600">Valid:</span>
                <span class="font-medium">{{ props.import?.valid_rows ?? "—" }}</span>
              </div>
            </div>
            <div class="mt-3 flex items-center gap-2">
              <Button
                variant="secondary"
                size="sm"
                class="text-xs"
                @click.prevent="openPreview"
              >
                Ogled CSV
              </Button>
              <Button
                v-if="canShowMissingButton"
                variant="default"
                size="sm"
                class="bg-indigo-600 hover:bg-indigo-700 text-xs"
                @click.prevent="openMissingContracts"
                title="Prikaži aktivne pogodbe, ki niso bile prisotne v uvozu (samo keyref)"
              >
                Ogled manjkajoče
              </Button>
              <Button
                v-if="isCompleted && contractRefIsKeyref"
                variant="default"
                size="sm"
                class="bg-amber-600 hover:bg-amber-700 text-xs"
                @click.prevent="openUnresolved"
                title="Prikaži vrstice, kjer pogodba (keyref) ni bila najdena"
              >
                Neobstoječi
              </Button>
            </div>
          </div>
          <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
            <TemplateControls
              :is-completed="isCompleted"
              :has-header="hasHeader"
              :delimiter-state="delimiterState"
              :selected-template-option="selectedTemplateOption"
              :filtered-templates="filteredTemplates"
              :template-applied="templateApplied"
              :form="form"
              @preview="openPreview"
              @update:hasHeader="
                (val) => {
                  hasHeader = val;
                  fetchColumns();
                }
              "
              @update:delimiterMode="
                (val) => {
                  delimiterState.mode = val;
                  fetchColumns();
                }
              "
              @update:delimiterCustom="
                (val) => {
                  delimiterState.custom = val;
                  fetchColumns();
                }
              "
              @apply-template="applyTemplateToImport"
            />
            <!-- Import options -->
            <div
              v-if="!isCompleted"
              class="mt-2 p-4 rounded-lg border bg-linear-to-br from-gray-50 to-gray-100"
            >
              <div class="flex items-start gap-3">
                <Checkbox
                  :id="'show-missing-checkbox'"
                  :checked="showMissingEnabled"
                  :model-value="showMissingEnabled"
                  @update:model-value="
                    (val) => {
                      showMissingEnabled = val;
                      saveImportOptions();
                    }
                  "
                />
                <div class="flex-1">
                  <Label
                    :for="'show-missing-checkbox'"
                    class="text-sm font-medium text-gray-700 cursor-pointer"
                  >
                    Seznam manjkajočih (po končanem uvozu)
                  </Label>
                  <p class="mt-1 text-xs text-gray-500">
                    Ko je omogočeno in je "contract.reference" nastavljen na keyref, bo po
                    končanem uvozu na voljo gumb za ogled pogodb, ki jih ni v datoteki.
                  </p>
                </div>
              </div>
            </div>
            <ChecklistSteps :steps="stepStates" :missing-critical="missingCritical" />
          </div>

          <ActionsBar
            :import-id="importId"
            :is-completed="isCompleted"
            :processing="processing"
            :saving-mappings="savingMappings"
            :can-process="canProcess"
            :selected-mappings-count="selectedMappingsCount"
            @preview="openPreview"
            @download="downloadImport"
            @save-mappings="saveMappings"
            @process-import="processImport"
            @simulate="openSimulation"
          />
          <div class="mt-2 text-xs text-gray-600" v-if="!importId">Import not found.</div>
          <div
            class="mt-2 text-xs text-gray-600"
            v-else-if="importId && !hasPersistedMappings && !isCompleted"
          >
            Apply a template or select Entity and Field for one or more columns, then
            click Save Mappings to enable processing.
          </div>

          <SavedMappingsTable :mappings="persistedMappings" />

          <MappingTable
            v-if="!isCompleted && displayRows.length"
            :rows="displayRows"
            :entity-options="entityOptions"
            :is-completed="isCompleted"
            :detected="detected"
            :detected-note="detectedNote"
            :duplicate-targets="duplicateTargets"
            :missing-critical="missingCritical"
            :mapping-saved="mappingSaved"
            :mapping-saved-count="mappingSavedCount"
            :mapping-error="mappingError"
            :fields-for-entity="fieldsForEntity"
          />

          <div v-else-if="!isCompleted" class="pt-4">
            <h3 class="font-semibold mb-2">Detected Columns</h3>
            <p class="text-sm text-gray-600">
              No columns detected.
              {{
                detectedNote ||
                "Preview is available for CSV/TXT files. You can still apply a template or use the saved mappings below."
              }}
            </p>
          </div>

          <ProcessResult :result="processResult" />

          <LogsTable
            :events="events"
            :loading="loadingEvents"
            :limit="eventsLimit"
            @update:limit="(val) => (eventsLimit = val)"
            @refresh="fetchEvents"
          />
        </div>
      </div>
    </div>
  </AppLayout>
  <CsvPreviewModal
    :show="showPreview"
    :columns="previewColumns"
    :rows="previewRows"
    :limit="previewLimit"
    :loading="previewLoading"
    :truncated="previewTruncated"
    :has-header="detected.has_header"
    @close="showPreview = false"
    @change-limit="
      async (val) => {
        previewLimit = val;
        await fetchPreview();
      }
    "
    @refresh="fetchPreview"
  />

  <MissingContractsModal
    :show="showMissingContracts"
    :loading="missingContractsLoading"
    :contracts="missingContracts"
    :format-money="formatMoney"
    @close="showMissingContracts = false"
  />

  <FoundContractsModal
    :show="showFoundContracts"
    :contracts="historyFoundContracts"
    @close="showFoundContracts = false"
  />

  <UnresolvedRowsModal
    :show="showUnresolved"
    :loading="unresolvedLoading"
    :columns="unresolvedColumns"
    :rows="unresolvedRows"
    :import-id="importId"
    @close="showUnresolved = false"
  />
  <SimulationModal
    :show="showPaymentSim"
    :rows="paymentSimRows"
    :limit="paymentSimLimit"
    :loading="paymentSimLoading"
    :summary="paymentSimSummary"
    :summary-sl="paymentSimSummarySl"
    :verbose="paymentSimVerbose"
    :entities="paymentSimEntities"
    :money-formatter="formatMoney"
    @close="showPaymentSim = false"
    @change-limit="
      async (val) => {
        paymentSimLimit = val;
        await fetchSimulation();
      }
    "
    @toggle-verbose="
      async () => {
        paymentSimVerbose = !paymentSimVerbose;
        await fetchSimulation();
      }
    "
    @refresh="fetchSimulation"
  />
</template>

<style scoped></style>
