<script>
  import { untrack } from 'svelte';
  import Modal from '$lib/components/Modal.svelte';
  import { success as toastSuccess, error as toastError } from '$lib/utils/toast.js';
  import {
    createTestMap,
    updateTestMap,
    fetchTestMapDetailsByTestMap,
    batchCreateTestMapDetails,
    batchUpdateTestMapDetails,
    batchDeleteTestMapDetails,
  } from '$lib/api/testmap.js';
  import { fetchHostApps, fetchSites, fetchWorkstations } from '$lib/api/organization.js';
  import { fetchEquipmentList } from '$lib/api/equipment.js';
  import {
    Plus,
    Trash2,
    Server,
    Monitor,
    FlaskConical,
    AlertCircle,
  } from 'lucide-svelte';

  let { open = $bindable(false), mode = 'create', initialData = null, groupData = null, containers = [], loading = false, onSave } = $props();

  let saving = $state(false);
  let localLoading = $state(false);

  // Modal context (shared for all rows)
  let modalContext = $state({
    TestMapID: null,
    HostType: '',
    HostID: '',
    ClientType: '',
    ClientID: '',
  });

  // Editable rows in modal
  let modalRows = $state([]);

  // Original rows for comparison in edit mode
  let originalRows = $state([]);

  // Form errors
  let formErrors = $state({});

  // Track previous mode and groupData to detect actual changes
  let previousMode = $state(mode);
  let previousGroupData = $state(null);
  let previousHostType = $state('');
  let previousClientType = $state('');

  // Host apps for HIS dropdown
  let hostApps = $state([]);

  // Dropdown data from APIs
  let sites = $state([]);
  let workstations = $state([]);
  let instruments = $state([]);

  const hostTypes = ['HIS', 'SITE', 'WST', 'INST'];
  const clientTypes = ['HIS', 'SITE', 'WST', 'INST'];

  async function fetchDropdownData() {
    try {
      const [hostAppsRes, sitesRes, workstationsRes, instrumentsRes] = await Promise.all([
        fetchHostApps(),
        fetchSites(),
        fetchWorkstations(),
        fetchEquipmentList(),
      ]);
      hostApps = hostAppsRes.data?.map((h) => ({ id: h.HostAppID, name: h.HostAppName })) || [];
      sites = sitesRes.data?.map((s) => ({ id: s.SiteID?.toString(), name: s.SiteName })) || [];
      workstations = workstationsRes.data?.map((w) => ({ id: w.WorkstationID?.toString(), name: w.WorkstationName })) || [];
      instruments = instrumentsRes.data?.map((i) => ({ id: i.EID?.toString(), name: i.InstrumentName || i.IEID })) || [];
    } catch (err) {
      console.error('Error fetching dropdown data:', err);
      toastError('Failed to load dropdown options');
    }
  }

  function getClientOptions(clientType) {
    switch (clientType) {
      case 'HIS': return hostApps;
      case 'SITE': return sites;
      case 'WST': return workstations;
      case 'INST': return instruments;
      default: return [];
    }
  }

  function getHostOptions(hostType) {
    switch (hostType) {
      case 'HIS': return hostApps;
      case 'SITE': return sites;
      case 'WST': return workstations;
      case 'INST': return instruments;
      default: return [];
    }
  }

  // Track if modal was ever opened
  let hasInitialized = $state(false);

  // Initialize modal when open changes to true, or when mode/groupData actually change
  $effect(() => {
    const isOpen = open;
    const currentMode = mode;
    const currentGroupData = groupData;

    if (!isOpen) {
      hasInitialized = false;
      return;
    }

    // Initialize on first open or when mode/groupData changes
    const shouldInitialize =
      !hasInitialized ||
      currentMode !== untrack(() => previousMode) ||
      currentGroupData !== untrack(() => previousGroupData);

    if (shouldInitialize) {
      hasInitialized = true;
      previousMode = currentMode;
      previousGroupData = currentGroupData;
      initializeModal();
    }
  });

  // Clear HostID when HostType changes in create mode
  $effect(() => {
    const currentHostType = modalContext.HostType;
    if (mode === 'create' && currentHostType !== previousHostType) {
      previousHostType = currentHostType;
      modalContext.HostID = '';
    }
  });

  // Clear ClientID when ClientType changes in create mode
  $effect(() => {
    const currentClientType = modalContext.ClientType;
    if (mode === 'create' && currentClientType !== previousClientType) {
      previousClientType = currentClientType;
      modalContext.ClientID = '';
    }
  });

  async function initializeModal() {
    formErrors = {};
    originalRows = [];

    // Fetch all dropdown data
    await fetchDropdownData();

    if (mode === 'edit' && groupData) {
      // Edit mode with group data - load all mappings in the group
      modalContext = {
        TestMapID: groupData.TestMapID || null,
        HostType: groupData.HostType || '',
        HostID: groupData.HostID || '',
        ClientType: groupData.ClientType || '',
        ClientID: groupData.ClientID || '',
      };

      // Fetch existing details from API
      fetchDetailsForGroup();
    } else if (mode === 'edit' && initialData) {
      // Legacy edit mode (single mapping)
      modalContext = {
        TestMapID: initialData.TestMapID || null,
        HostType: initialData.HostType || '',
        HostID: initialData.HostID || '',
        ClientType: initialData.ClientType || '',
        ClientID: initialData.ClientID || '',
      };
      modalRows = [{
        TestMapDetailID: null,
        TestMapID: initialData.TestMapID,
        TestSiteID: null,
        HostTestCode: initialData.HostTestCode || '',
        HostTestName: initialData.HostTestName || '',
        ConDefID: initialData.ConDefID || null,
        ClientTestCode: initialData.ClientTestCode || '',
        ClientTestName: initialData.ClientTestName || '',
        isNew: false,
      }];
      originalRows = JSON.parse(JSON.stringify(modalRows));
    } else {
      // Create mode
      modalContext = {
        TestMapID: null,
        HostType: '',
        HostID: '',
        ClientType: '',
        ClientID: '',
      };
      modalRows = [createEmptyRow()];
    }
  }

  async function fetchDetailsForGroup() {
    if (!modalContext.TestMapID) return;

    localLoading = true;
    try {
      const response = await fetchTestMapDetailsByTestMap(modalContext.TestMapID);
      const details = response.data || [];

      modalRows = details.map((detail) => ({
        TestMapDetailID: parseInt(detail.TestMapDetailID) || null,
        TestMapID: parseInt(detail.TestMapID) || null,
        TestSiteID: detail.TestSiteID ? parseInt(detail.TestSiteID) : null,
        HostTestCode: detail.HostTestCode || '',
        HostTestName: detail.HostTestName || '',
        ConDefID: detail.ConDefID ? parseInt(detail.ConDefID) : null,
        ClientTestCode: detail.ClientTestCode || '',
        ClientTestName: detail.ClientTestName || '',
        isNew: false,
      }));

      // Store a copy for comparison
      originalRows = JSON.parse(JSON.stringify(modalRows));
    } catch (err) {
      toastError(err.message || 'Failed to fetch test map details');
      modalRows = [];
      originalRows = [];
    } finally {
      localLoading = false;
    }
  }

  function createEmptyRow() {
    return {
      TestMapDetailID: null,
      TestMapID: null,
      TestSiteID: null,
      HostTestCode: '',
      HostTestName: '',
      ConDefID: null,
      ClientTestCode: '',
      ClientTestName: '',
      isNew: true,
    };
  }

  function addMappingRow() {
    modalRows = [...modalRows, createEmptyRow()];
  }

  function removeMappingRow(index) {
    modalRows = modalRows.filter((_, i) => i !== index);
  }

  function updateRowField(index, field, value) {
    modalRows = modalRows.map((row, i) => {
      if (i === index) {
        return { ...row, [field]: value };
      }
      return row;
    });
  }

  function validateModal() {
    const errors = {};

    if (!modalContext.HostType) {
      errors.HostType = 'Host Type is required';
    }
    if (!modalContext.HostID) {
      errors.HostID = 'Host ID is required';
    }
    if (!modalContext.ClientType) {
      errors.ClientType = 'Client Type is required';
    }
    if (!modalContext.ClientID) {
      errors.ClientID = 'Client ID is required';
    }

    // Validate each row
    const rowErrors = [];
    modalRows.forEach((row, index) => {
      const rowError = {};
      const hasAnyField = row.HostTestCode || row.HostTestName || row.ClientTestCode || row.ClientTestName;
      if (!hasAnyField) {
        rowError.empty = 'At least one field must be filled';
      }
      if (modalContext.ClientType === 'INST' && !row.ConDefID) {
        rowError.ConDefID = 'Container is required for INST type';
      }
      if (Object.keys(rowError).length > 0) {
        rowErrors[index] = rowError;
      }
    });

    if (rowErrors.length > 0) {
      errors.rows = rowErrors;
    }

    formErrors = errors;
    return Object.keys(errors).length === 0;
  }

  async function handleSave() {
    if (!validateModal()) {
      return;
    }

    if (mode === 'create') {
      await handleCreate();
    } else {
      await handleUpdate();
    }
  }

  async function handleCreate() {
    saving = true;
    try {
      // Create header with details array
      const payload = {
        HostType: modalContext.HostType,
        HostID: modalContext.HostID,
        ClientType: modalContext.ClientType,
        ClientID: modalContext.ClientID,
        details: modalRows.map((row) => ({
          HostTestCode: row.HostTestCode,
          HostTestName: row.HostTestName,
          ConDefID: modalContext.ClientType === 'INST' ? row.ConDefID : null,
          ClientTestCode: row.ClientTestCode,
          ClientTestName: row.ClientTestName,
        })),
      };

      await createTestMap(payload);
      toastSuccess(`Test mapping${modalRows.length > 1 ? 's' : ''} created successfully`);
      open = false;
      onSave?.();
    } catch (err) {
      toastError(err.message || 'Failed to create test mapping');
    } finally {
      saving = false;
    }
  }

  async function handleUpdate() {
    saving = true;
    try {
      // Compare current rows with original rows
      const currentIds = new Set(modalRows.map((r) => r.TestMapDetailID).filter((id) => id !== null));
      const originalIds = new Set(originalRows.map((r) => r.TestMapDetailID).filter((id) => id !== null));

      // Find deleted IDs (in original but not in current)
      const deletedIds = [...originalIds].filter((id) => !currentIds.has(id));

      // Find new rows (no TestMapDetailID)
      const newRows = modalRows.filter((row) => row.TestMapDetailID === null);

      // Find updated rows (has TestMapDetailID and exists in original)
      const updatedRows = modalRows.filter((row) => {
        if (row.TestMapDetailID === null) return false;
        const original = originalRows.find((r) => r.TestMapDetailID === row.TestMapDetailID);
        if (!original) return false;
        // Check if any field changed
        return (
          row.HostTestCode !== original.HostTestCode ||
          row.HostTestName !== original.HostTestName ||
          row.ConDefID !== original.ConDefID ||
          row.ClientTestCode !== original.ClientTestCode ||
          row.ClientTestName !== original.ClientTestName
        );
      });

      // Execute batch operations
      const promises = [];

      if (deletedIds.length > 0) {
        promises.push(batchDeleteTestMapDetails(deletedIds));
      }

      if (newRows.length > 0) {
        const newDetails = newRows.map((row) => ({
          TestMapID: modalContext.TestMapID,
          HostTestCode: row.HostTestCode,
          HostTestName: row.HostTestName,
          ConDefID: modalContext.ClientType === 'INST' ? row.ConDefID : null,
          ClientTestCode: row.ClientTestCode,
          ClientTestName: row.ClientTestName,
        }));
        promises.push(batchCreateTestMapDetails(newDetails));
      }

      if (updatedRows.length > 0) {
        const updatedDetails = updatedRows.map((row) => ({
          TestMapDetailID: row.TestMapDetailID,
          TestMapID: modalContext.TestMapID,
          HostTestCode: row.HostTestCode,
          HostTestName: row.HostTestName,
          ConDefID: modalContext.ClientType === 'INST' ? row.ConDefID : null,
          ClientTestCode: row.ClientTestCode,
          ClientTestName: row.ClientTestName,
        }));
        promises.push(batchUpdateTestMapDetails(updatedDetails));
      }

      if (promises.length > 0) {
        await Promise.all(promises);
      }

      toastSuccess(`Test mapping${modalRows.length > 1 ? 's' : ''} updated successfully`);
      open = false;
      onSave?.();
    } catch (err) {
      toastError(err.message || 'Failed to update test mapping');
    } finally {
      saving = false;
    }
  }

  function getContainerName(conDefId) {
    if (!conDefId) return '-';
    const container = containers.find((c) => c.ConDefID === conDefId);
    return container ? container.ConName : '-';
  }
</script>

<Modal
  bind:open
  title={mode === 'create' ? 'Add Test Mapping' : `Edit Test Mapping${groupData ? 's' : ''} (${modalRows.length})`}
  size="xl"
>
  <div class="flex flex-col max-h-[70vh] relative">
    <!-- Loading Overlay -->
    {#if loading || localLoading}
      <div class="absolute inset-0 bg-base-100/80 z-50 flex items-center justify-center">
        <div class="flex flex-col items-center gap-2">
          <span class="loading loading-spinner loading-lg text-primary"></span>
          <span class="text-sm text-gray-500">Loading test mappings...</span>
        </div>
      </div>
    {/if}
    <!-- Sticky Top Section: Info banner + Host and Client -->
    <div class="flex-shrink-0 bg-base-100 z-10">
      <!-- Info banner for group editing -->
      {#if mode === 'edit' && groupData}
        <div class="alert alert-info alert-sm mb-4">
          <AlertCircle class="w-4 h-4" />
          <div>
            <span class="font-medium">Editing {modalRows.length} test mapping(s)</span>
            <span class="text-sm opacity-80">in this host-client group</span>
          </div>
        </div>
      {/if}

      <!-- Top Section: Host and Client side-by-side -->
      <div class="grid grid-cols-1 md:grid-cols-2 gap-4 border-b border-base-300 pb-4">
        <!-- Host Section -->
        <div class="space-y-2">
          <h3 class="text-xs font-medium text-gray-600 uppercase tracking-wide flex items-center gap-1">
            <Server class="w-3 h-3" />
            Host
          </h3>

          <div class="grid grid-cols-2 gap-2">
            <div class="form-control">
              <label class="label text-xs" for="hostType">
                <span class="label-text">Type</span>
                <span class="label-text-alt text-error">*</span>
              </label>
              <select
                id="hostType"
                class="select select-xs select-bordered"
                bind:value={modalContext.HostType}
                disabled={mode === 'edit'}
              >
                <option value="">Select...</option>
                {#each hostTypes as type (type)}
                  <option value={type}>{type}</option>
                {/each}
              </select>
              {#if formErrors.HostType}
                <span class="text-xs text-error">{formErrors.HostType}</span>
              {/if}
            </div>

            <div class="form-control">
              <label class="label text-xs" for="hostID">
                <span class="label-text">ID</span>
                <span class="label-text-alt text-error">*</span>
              </label>
              {#if mode === 'create' && ['HIS', 'SITE', 'WST', 'INST'].includes(modalContext.HostType)}
                <select
                  id="hostID"
                  class="select select-xs select-bordered"
                  bind:value={modalContext.HostID}
                >
                  <option value="">Select...</option>
                  {#each getHostOptions(modalContext.HostType) as option (option.id)}
                    <option value={option.id}>{option.name} ({option.id})</option>
                  {/each}
                </select>
              {:else}
                <input
                  id="hostID"
                  type="text"
                  class="input input-xs input-bordered"
                  bind:value={modalContext.HostID}
                  placeholder="Host ID"
                  disabled={mode === 'edit'}
                />
              {/if}
              {#if formErrors.HostID}
                <span class="text-xs text-error">{formErrors.HostID}</span>
              {/if}
            </div>
          </div>
          {#if mode === 'edit' && groupData?.HostName}
            <div class="text-xs text-gray-600 mt-1">{groupData.HostName}</div>
          {/if}
        </div>

        <!-- Client Section -->
        <div class="space-y-2">
          <h3 class="text-xs font-medium text-gray-600 uppercase tracking-wide flex items-center gap-1">
            <Monitor class="w-3 h-3" />
            Client
          </h3>

          <div class="grid grid-cols-2 gap-2">
            <div class="form-control">
              <label class="label text-xs" for="clientType">
                <span class="label-text">Type</span>
                <span class="label-text-alt text-error">*</span>
              </label>
              <select
                id="clientType"
                class="select select-xs select-bordered"
                bind:value={modalContext.ClientType}
                disabled={mode === 'edit'}
              >
                <option value="">Select...</option>
                {#each clientTypes as type (type)}
                  <option value={type}>{type}</option>
                {/each}
              </select>
              {#if formErrors.ClientType}
                <span class="text-xs text-error">{formErrors.ClientType}</span>
              {/if}
            </div>

            <div class="form-control">
              <label class="label text-xs" for="clientID">
                <span class="label-text">ID</span>
                <span class="label-text-alt text-error">*</span>
              </label>
              {#if mode === 'create' && ['HIS', 'SITE', 'WST', 'INST'].includes(modalContext.ClientType)}
                <select
                  id="clientID"
                  class="select select-xs select-bordered"
                  bind:value={modalContext.ClientID}
                >
                  <option value="">Select...</option>
                  {#each getClientOptions(modalContext.ClientType) as option (option.id)}
                    <option value={option.id}>{option.name} ({option.id})</option>
                  {/each}
                </select>
              {:else}
                <input
                  id="clientID"
                  type="text"
                  class="input input-xs input-bordered"
                  bind:value={modalContext.ClientID}
                  placeholder="Client ID"
                  disabled={mode === 'edit'}
                />
              {/if}
              {#if formErrors.ClientID}
                <span class="text-xs text-error">{formErrors.ClientID}</span>
              {/if}
            </div>
          </div>
          {#if mode === 'edit' && groupData?.ClientName}
            <div class="text-xs text-gray-600 mt-1">{groupData.ClientName}</div>
          {/if}
        </div>
      </div>
    </div>

    <!-- Scrollable Middle Section: Editable Table -->
    <div class="flex-1 overflow-y-auto space-y-1 py-2">
      <h3 class="text-xs font-medium text-gray-600 uppercase tracking-wide flex items-center gap-1">
        <FlaskConical class="w-3 h-3" />
        Test Mappings
        <span class="text-gray-400 font-normal">({modalRows.length} row{modalRows.length !== 1 ? 's' : ''})</span>
      </h3>

      <div class="overflow-x-auto border border-base-300 rounded-lg">
        <table class="table table-compact w-full [&_td]:py-1 [&_th]:py-1">
          <thead class="bg-base-200">
            <tr>
              <th class="text-xs px-2">Host Test Code</th>
              <th class="text-xs px-2">Host Test Name</th>
              <th class="text-xs px-2">Client Test Code</th>
              <th class="text-xs px-2">Client Test Name</th>
              <th class="text-xs w-48 px-2">Container</th>
              <th class="text-xs w-10 px-2"></th>
            </tr>
          </thead>
          <tbody>
            {#each modalRows as row, index (index)}
              <tr class="hover:bg-base-100">
                <td class="px-2">
                  <input
                    type="text"
                    class="input input-xs input-bordered w-full m-0"
                    bind:value={row.HostTestCode}
                    placeholder="Code"
                  />
                </td>
                <td class="px-2">
                  <input
                    type="text"
                    class="input input-xs input-bordered w-full m-0"
                    bind:value={row.HostTestName}
                    placeholder="Name"
                  />
                </td>
                <td class="px-2">
                  <input
                    type="text"
                    class="input input-xs input-bordered w-full m-0"
                    bind:value={row.ClientTestCode}
                    placeholder="Code"
                  />
                </td>
                <td class="px-2">
                  <input
                    type="text"
                    class="input input-xs input-bordered w-full m-0"
                    bind:value={row.ClientTestName}
                    placeholder="Name"
                  />
                </td>
                <td class="px-2">
                  {#if modalContext.ClientType === 'INST'}
                    <select
                      class="select select-xs select-bordered w-full m-0"
                      value={row.ConDefID}
                      onchange={(e) => updateRowField(index, 'ConDefID', e.target.value === '' ? null : parseInt(e.target.value))}
                    >
                      <option value="">Select container...</option>
                      {#each containers as container (container.ConDefID)}
                        <option value={parseInt(container.ConDefID)}>
                          {container.ConName}
                        </option>
                      {/each}
                    </select>
                    {#if formErrors.rows?.[index]?.ConDefID}
                      <span class="text-xs text-error">{formErrors.rows[index].ConDefID}</span>
                    {/if}
                  {:else}
                    <span></span>
                  {/if}
                </td>
                <td class="px-2">
                  <button
                    class="btn btn-ghost btn-xs text-error"
                    onclick={() => removeMappingRow(index)}
                    disabled={modalRows.length === 1 && mode === 'edit'}
                    title="Remove row"
                  >
                    <Trash2 class="w-3 h-3" />
                  </button>
                </td>
              </tr>
            {/each}
          </tbody>
        </table>
      </div>

      {#if formErrors.rows}
        {#each Object.entries(formErrors.rows) as [idx, error] (idx)}
          {#if error.empty}
            <p class="text-xs text-error">Row {parseInt(idx) + 1}: {error.empty}</p>
          {/if}
        {/each}
      {/if}
    </div>

    <!-- Sticky Bottom Section: Add Button -->
    <div class="flex-shrink-0 flex justify-center pt-2 border-t border-base-300 bg-base-100 pb-2">
      <button class="btn btn-sm btn-outline" onclick={addMappingRow}>
        <Plus class="w-4 h-4 mr-1" />
        Add Mapping
      </button>
    </div>
  </div>

  {#snippet footer()}
    <button class="btn btn-ghost" onclick={() => (open = false)} type="button" disabled={saving}>
      Cancel
    </button>
    <button class="btn btn-primary" onclick={handleSave} disabled={saving} type="button">
      {#if saving}
        <span class="loading loading-spinner loading-sm mr-2"></span>
      {/if}
      {saving ? 'Saving...' : `Save ${modalRows.length} Mapping${modalRows.length !== 1 ? 's' : ''}`}
    </button>
  {/snippet}
</Modal>
