<?= $this->extend("v2/layout/main_layout"); ?>

<?= $this->section("content") ?>
<div x-data="tests()" x-init="init()">

  <!-- Page Header -->
  <div class="card-glass p-6 animate-fadeIn mb-6">
    <div class="flex items-center gap-4">
      <div
        class="w-14 h-14 rounded-2xl bg-gradient-to-br from-indigo-600 to-indigo-900 flex items-center justify-center shadow-lg">
        <i class="fa-solid fa-microscope text-2xl text-white"></i>
      </div>
      <div>
        <h2 class="text-2xl font-bold" style="color: rgb(var(--color-text));">Laboratory Tests</h2>
        <p class="text-sm" style="color: rgb(var(--color-text-muted));">Manage test definitions, parameters, and groups
        </p>
      </div>
    </div>
  </div>

  <!-- Search & Actions Bar -->
  <div class="card mb-6">
    <div class="p-4">
      <div class="flex flex-col sm:flex-row gap-4 items-center justify-between">
        <div class="flex w-full sm:w-auto gap-2">
          <input type="text" placeholder="Search tests..." class="input flex-1 sm:w-80" x-model="keyword"
            @keyup.enter="fetchList()" />
          <select class="select w-40" x-model="filterType" @change="fetchList()">
            <option value="">All Types</option>
            <template x-for="(type, index) in (testTypes || [])" :key="(type?.VID ?? index)">
              <option :value="type?.VID" x-text="(type?.VValue || '') + ' - ' + (type?.VDesc || '')"></option>
            </template>
          </select>
          <button class="btn btn-primary" @click="fetchList()">
            <i class="fa-solid fa-search"></i>
          </button>
        </div>
        <button class="btn btn-primary w-full sm:w-auto" @click="showForm()">
          <i class="fa-solid fa-plus mr-2"></i>
          Add Test
        </button>
      </div>
    </div>
  </div>

  <!-- Content Card -->
  <div class="card overflow-hidden">
    <!-- Loading State -->
    <div x-show="loading" class="p-12 text-center" x-cloak>
      <div class="spinner spinner-lg mx-auto mb-4"></div>
      <p style="color: rgb(var(--color-text-muted));">Loading tests...</p>
    </div>

    <!-- Table -->
    <div class="overflow-x-auto" x-show="!loading">
      <table class="table">
        <thead>
          <tr>
            <th>ID</th>
            <th>Code</th>
            <th>Test Name</th>
            <th>Type</th>
            <th>Seq</th>
            <th>Visible</th>
            <th class="text-center">Actions</th>
          </tr>
        </thead>
        <tbody>
          <!-- Empty State -->
          <template x-if="!list || list.length === 0">
            <tr>
              <td colspan="7" class="text-center py-12">
                <div class="flex flex-col items-center gap-3" style="color: rgb(var(--color-text-muted));">
                  <i class="fa-solid fa-inbox text-5xl opacity-40"></i>
                  <p class="text-lg">No tests found</p>
                  <button class="btn btn-primary btn-sm mt-2" @click="showForm()">
                    <i class="fa-solid fa-plus mr-1"></i> Add First Test
                  </button>
                </div>
              </td>
            </tr>
          </template>

          <!-- Test Rows -->
          <template x-for="test in list" :key="test.TestSiteID">
            <tr class="hover:bg-opacity-50">
              <td>
                <span class="badge badge-ghost font-mono text-xs" x-text="test.TestSiteID || '-'"></span>
              </td>
              <td>
                <code class="text-sm font-mono px-2 py-1 rounded"
                  style="background: rgb(var(--color-bg-secondary)); color: rgb(var(--color-primary));"
                  x-text="test.TestSiteCode || '-'"></code>
              </td>
              <td>
                <div class="font-medium" style="color: rgb(var(--color-text));" x-text="test.TestSiteName || '-'"></div>
                <div class="text-xs mt-1" style="color: rgb(var(--color-text-muted));" x-text="test.Description || ''">
                </div>
              </td>
              <td>
                <span class="badge" :class="{
                        'badge-info': test.TypeCode === 'TEST',
                        'badge-success': test.TypeCode === 'PARAM',
                        'badge-warning': test.TypeCode === 'CALC',
                        'badge-primary': test.TypeCode === 'GROUP',
                        'badge-secondary': test.TypeCode === 'TITLE'
                      }" x-text="test.TypeName || test.TypeCode || '-'"></span>
              </td>
              <td x-text="test.SeqScr || '-'"></td>
              <td>
                <div class="flex gap-1">
                  <span class="badge badge-ghost badge-xs" title="Screen" x-show="test.VisibleScr == 1">
                    <i class="fa-solid fa-desktop text-green-500"></i>
                  </span>
                  <span class="badge badge-ghost badge-xs" title="Report" x-show="test.VisibleRpt == 1">
                    <i class="fa-solid fa-file-alt text-blue-500"></i>
                  </span>
                </div>
              </td>
              <td class="text-center">
                <div class="flex items-center justify-center gap-1">
                  <button class="btn btn-ghost btn-sm btn-square" @click="viewTest(test.TestSiteID)"
                    title="View Details">
                    <i class="fa-solid fa-eye text-sky-500"></i>
                  </button>
                  <button class="btn btn-ghost btn-sm btn-square" @click="editTest(test.TestSiteID)" title="Edit">
                    <i class="fa-solid fa-pen text-indigo-500"></i>
                  </button>
                  <button class="btn btn-ghost btn-sm btn-square" @click="confirmDelete(test)" title="Delete">
                    <i class="fa-solid fa-trash" style="color: rgb(var(--color-error));"></i>
                  </button>
                </div>
              </td>
            </tr>
          </template>
        </tbody>
      </table>
    </div>

    <!-- Summary -->
    <div class="p-4 flex items-center justify-between" style="border-top: 1px solid rgb(var(--color-border));"
      x-show="list && list.length > 0">
      <span class="text-sm" style="color: rgb(var(--color-text-muted));"
        x-text="'Showing ' + list.length + ' tests'"></span>
    </div>
  </div>

  <!-- Include Form Dialogs -->
  <?= $this->include('v2/master/tests/test_dialog') ?>
  <?= $this->include('v2/master/tests/param_dialog') ?>
  <?= $this->include('v2/master/tests/calc_dialog') ?>
  <?= $this->include('v2/master/tests/grp_dialog') ?>

  <!-- View Details Modal -->
  <div x-show="showViewModal" x-cloak class="modal-overlay" @click.self="showViewModal = false">
    <div class="modal-content p-6 max-w-4xl w-full max-h-[90vh] overflow-y-auto"
      x-transition:enter="transition ease-out duration-200" x-transition:enter-start="opacity-0"
      x-transition:enter-end="opacity-100" x-transition:leave="transition ease-in duration-150"
      x-transition:leave-start="opacity-100" x-transition:leave-end="opacity-0">
      <!-- Header -->
      <div class="flex items-center justify-between mb-6">
        <h3 class="font-bold text-xl flex items-center gap-2" style="color: rgb(var(--color-text));">
          <i class="fa-solid fa-microscope" style="color: rgb(var(--color-primary));"></i>
          <span x-text="viewData?.TestSiteName || 'Test Details'"></span>
        </h3>
        <button class="btn btn-ghost btn-sm btn-square" @click="showViewModal = false">
          <i class="fa-solid fa-times"></i>
        </button>
      </div>

      <!-- Details Content -->
      <template x-if="viewData">
        <div class="space-y-6">
          <!-- Basic Info -->
          <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
            <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
              <div class="text-xs" style="color: rgb(var(--color-text-muted));">Code</div>
              <div class="font-mono font-semibold" x-text="viewData.TestSiteCode || '-'"></div>
            </div>
            <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
              <div class="text-xs" style="color: rgb(var(--color-text-muted));">Type</div>
              <span class="badge" :class="{
                      'badge-info': viewData.TypeCode === 'TEST',
                      'badge-success': viewData.TypeCode === 'PARAM',
                      'badge-warning': viewData.TypeCode === 'CALC',
                      'badge-primary': viewData.TypeCode === 'GROUP',
                      'badge-secondary': viewData.TypeCode === 'TITLE'
                    }" x-text="viewData.TypeName || viewData.TypeCode || '-'"></span>
            </div>
            <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
              <div class="text-xs" style="color: rgb(var(--color-text-muted));">Sequence (Screen)</div>
              <div class="font-semibold" x-text="viewData.SeqScr || '-'"></div>
            </div>
            <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
              <div class="text-xs" style="color: rgb(var(--color-text-muted));">Sequence (Report)</div>
              <div class="font-semibold" x-text="viewData.SeqRpt || '-'"></div>
            </div>
          </div>

          <!-- Type-specific Details -->
          <template x-if="viewData.TypeCode === 'TEST' || viewData.TypeCode === 'PARAM'">
            <div>
              <h4 class="font-semibold mb-3 flex items-center gap-2">
                <i class="fa-solid fa-flask"></i> Technical Details
              </h4>
              <div class="grid grid-cols-2 md:grid-cols-4 gap-4">
                <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
                  <div class="text-xs" style="color: rgb(var(--color-text-muted));">Result Type</div>
                  <div x-text="viewData.testdeftech?.[0]?.ResultType || '-'"></div>
                </div>
                <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
                  <div class="text-xs" style="color: rgb(var(--color-text-muted));">Unit</div>
                  <div x-text="viewData.testdeftech?.[0]?.Unit1 || '-'"></div>
                </div>
                <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
                  <div class="text-xs" style="color: rgb(var(--color-text-muted));">Method</div>
                  <div x-text="viewData.testdeftech?.[0]?.Method || '-'"></div>
                </div>
                <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
                  <div class="text-xs" style="color: rgb(var(--color-text-muted));">Expected TAT</div>
                  <div x-text="(viewData.testdeftech?.[0]?.ExpectedTAT || '-') + ' min'"></div>
                </div>
              </div>
            </div>
          </template>

          <template x-if="viewData.TypeCode === 'CALC'">
            <div>
              <h4 class="font-semibold mb-3 flex items-center gap-2">
                <i class="fa-solid fa-calculator"></i> Calculation Details
              </h4>
              <div class="grid grid-cols-2 gap-4">
                <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
                  <div class="text-xs" style="color: rgb(var(--color-text-muted));">Formula Input</div>
                  <div class="font-mono text-sm" x-text="viewData.testdefcal?.[0]?.FormulaInput || '-'"></div>
                </div>
                <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
                  <div class="text-xs" style="color: rgb(var(--color-text-muted));">Formula Code</div>
                  <div class="font-mono text-sm" x-text="viewData.testdefcal?.[0]?.FormulaCode || '-'"></div>
                </div>
                <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
                  <div class="text-xs" style="color: rgb(var(--color-text-muted));">Result Unit</div>
                  <div x-text="viewData.testdefcal?.[0]?.Unit1 || '-'"></div>
                </div>
                <div class="p-3 rounded-lg" style="background: rgb(var(--color-bg-secondary));">
                  <div class="text-xs" style="color: rgb(var(--color-text-muted));">Decimal</div>
                  <div x-text="viewData.testdefcal?.[0]?.Decimal || '-'"></div>
                </div>
              </div>
            </div>
          </template>

          <template x-if="viewData.TypeCode === 'GROUP'">
            <div>
              <h4 class="font-semibold mb-3 flex items-center gap-2">
                <i class="fa-solid fa-layer-group"></i> Group Members
              </h4>
              <div class="overflow-x-auto">
                <table class="table table-sm">
                  <thead>
                    <tr>
                      <th>Code</th>
                      <th>Name</th>
                      <th>Type</th>
                    </tr>
                  </thead>
                  <tbody>
                    <template x-for="member in (viewData.testdefgrp || [])" :key="member.TestGrpID">
                      <tr>
                        <td><code x-text="member.TestSiteCode"></code></td>
                        <td x-text="member.TestSiteName"></td>
                        <td>
                          <span class="badge badge-xs" :class="{
                                  'badge-info': member.MemberTypeCode === 'TEST',
                                  'badge-success': member.MemberTypeCode === 'PARAM'
                                }" x-text="member.MemberTypeCode"></span>
                        </td>
                      </tr>
                    </template>
                    <template x-if="!viewData.testdefgrp || viewData.testdefgrp.length === 0">
                      <tr>
                        <td colspan="3" class="text-center text-muted">No members</td>
                      </tr>
                    </template>
                  </tbody>
                </table>
              </div>
            </div>
          </template>

          <!-- Test Mappings -->
          <template x-if="viewData.testmap && viewData.testmap.length > 0">
            <div>
              <h4 class="font-semibold mb-3 flex items-center gap-2">
                <i class="fa-solid fa-link"></i> Test Mappings
              </h4>
              <div class="overflow-x-auto">
                <table class="table table-sm">
                  <thead>
                    <tr>
                      <th>Host Type</th>
                      <th>Host Code</th>
                      <th>Client Type</th>
                      <th>Client Code</th>
                    </tr>
                  </thead>
                  <tbody>
                    <template x-for="map in viewData.testmap" :key="map.TestMapID">
                      <tr>
                        <td x-text="map.HostType || '-'"></td>
                        <td x-text="map.HostTestCode || '-'"></td>
                        <td x-text="map.ClientType || '-'"></td>
                        <td><code x-text="map.ClientTestCode || '-'"></code></td>
                      </tr>
                    </template>
                  </tbody>
                </table>
              </div>
            </div>
          </template>
        </div>
      </template>

      <!-- Actions -->
      <div class="flex gap-3 mt-8 pt-6" style="border-top: 1px solid rgb(var(--color-border));">
        <button class="btn btn-ghost flex-1" @click="showViewModal = false">Close</button>
        <button class="btn btn-primary flex-1" @click="editTest(viewData?.TestSiteID)">
          <i class="fa-solid fa-edit mr-2"></i> Edit Test
        </button>
      </div>
    </div>
  </div>

  <!-- Delete Confirmation Modal -->
  <div x-show="showDeleteModal" x-cloak class="modal-overlay" @click.self="showDeleteModal = false">
    <div class="modal-content p-6 max-w-md">
      <h3 class="font-bold text-lg mb-4 flex items-center gap-2" style="color: rgb(var(--color-error));">
        <i class="fa-solid fa-exclamation-triangle"></i>
        Confirm Delete
      </h3>
      <p class="mb-6" style="color: rgb(var(--color-text));">
        Are you sure you want to delete test <strong x-text="deleteTarget?.TestSiteName"></strong>?
        This action cannot be undone.
      </p>
      <div class="flex gap-2">
        <button class="btn btn-ghost flex-1" @click="showDeleteModal = false">Cancel</button>
        <button class="btn flex-1" style="background: rgb(var(--color-error)); color: white;" @click="deleteTest()"
          :disabled="deleting">
          <span x-show="deleting" class="spinner spinner-sm"></span>
          <span x-show="!deleting">Delete</span>
        </button>
      </div>
    </div>
  </div>

</div>
<?= $this->endSection() ?>

<?= $this->section("script") ?>
<script>
  function tests() {
    return {
      // State
      loading: false,
      list: [],
      testTypes: [],
      keyword: "",
      filterType: "",

      // View Modal
      showViewModal: false,
      viewData: null,

      // Form Modal
      showModal: false,
      isEditing: false,
      saving: false,
      errors: {},
      activeTab: 'basic',

      // Member Selector
      showMemberSelector: false,
      memberSearch: '',
      availableTests: [],

      form: {
        TestSiteID: null,
        SiteID: 1,
        TestSiteCode: "",
        TestSiteName: "",
        TestType: "",
        Description: "",
        SeqScr: 0,
        SeqRpt: 0,
        IndentLeft: 0,
        VisibleScr: 1,
        VisibleRpt: 1,
        CountStat: 1,
        // Technical fields
        DisciplineID: "",
        DepartmentID: "",
        ResultType: "",
        RefType: "",
        Unit1: "",
        Unit2: "",
        Decimal: 2,
        Method: "",
        SampleType: "",
        ExpectedTAT: 60,
        RefMin: null,
        RefMax: null,
        RefText: "",
        CriticalLow: null,
        CriticalHigh: null,
        // Calculation fields
        FormulaInput: "",
        FormulaCode: "",
        // Value Set fields
        ValueSetID: "",
        DefaultValue: "",
        // Group members
        groupMembers: []
      },

      // Delete Modal
      showDeleteModal: false,
      deleteTarget: null,
      deleting: false,

      // Lifecycle
      async init() {
        await this.fetchTestTypes();
        await this.fetchAvailableTests();
        // Small delay to ensure Alpine has processed testTypes
        setTimeout(() => {
          this.fetchList();
        }, 50);
      },

      // Fetch test types from valueset
      async fetchTestTypes() {
        try {
          const res = await fetch(`${BASEURL}api/valuesetdef/27`, {
            credentials: 'include'
          });
          if (!res.ok) throw new Error("HTTP error");
          const data = await res.json();
          this.testTypes = data.data || [];
        } catch (err) {
          console.error('Failed to fetch test types:', err);
          // Fallback to hardcoded types
          this.testTypes = [
            { VID: 1, VValue: 'TEST', VDesc: 'Test' },
            { VID: 2, VValue: 'PARAM', VDesc: 'Parameter' },
            { VID: 3, VValue: 'CALC', VDesc: 'Calculated' },
            { VID: 4, VValue: 'GROUP', VDesc: 'Group' },
            { VID: 5, VValue: 'TITLE', VDesc: 'Title' }
          ];
        }
      },

      // Fetch available tests for group members
      async fetchAvailableTests() {
        try {
          const res = await fetch(`${BASEURL}api/tests`, {
            credentials: 'include'
          });
          if (!res.ok) throw new Error("HTTP error");
          const data = await res.json();
          this.availableTests = (data.data || []).filter(t =>
            t.TypeCode === 'TEST' || t.TypeCode === 'PARAM'
          );
        } catch (err) {
          console.error('Failed to fetch available tests:', err);
          this.availableTests = [];
        }
      },

      // Get type display name
      getTypeName(vid) {
        const code = this.getTypeCode(vid);
        const typeMap = {
          'TEST': 'Test',
          'PARAM': 'Parameter',
          'CALC': 'Calculated Test',
          'GROUP': 'Group',
          'TITLE': 'Title'
        };
        return typeMap[code] || 'Test';
      },

      // Fetch test list
      async fetchList() {
        this.loading = true;
        try {
          const params = new URLSearchParams();
          if (this.keyword) params.append('TestSiteName', this.keyword);
          if (this.filterType) params.append('TestType', this.filterType);

          const res = await fetch(`${BASEURL}api/tests?${params}`, {
            credentials: 'include'
          });
          if (!res.ok) throw new Error("HTTP error");
          const data = await res.json();

          this.list = data.data || [];
        } catch (err) {
          console.error(err);
          this.list = [];
        } finally {
          this.loading = false;
        }
      },

      // View test details
      async viewTest(id) {
        this.viewData = null;
        try {
          const res = await fetch(`${BASEURL}api/tests/${id}`, {
            credentials: 'include'
          });
          const data = await res.json();
          if (data.data) {
            this.viewData = data.data;
            this.showViewModal = true;
          }
        } catch (err) {
          console.error(err);
          alert('Failed to load test details');
        }
      },

      // Show form for new test
      showForm() {
        this.isEditing = false;
        this.activeTab = 'basic';
        this.form = {
          TestSiteID: null,
          SiteID: 1,
          TestSiteCode: "",
          TestSiteName: "",
          TestType: "",
          Description: "",
          SeqScr: 0,
          SeqRpt: 0,
          IndentLeft: 0,
          VisibleScr: 1,
          VisibleRpt: 1,
          CountStat: 1,
          DisciplineID: "",
          DepartmentID: "",
          ResultType: "",
          RefType: "",
          Unit1: "",
          Unit2: "",
          Decimal: 2,
          Method: "",
          SampleType: "",
          ExpectedTAT: 60,
          RefMin: null,
          RefMax: null,
          RefText: "",
          CriticalLow: null,
          CriticalHigh: null,
          FormulaInput: "",
          FormulaCode: "",
          ValueSetID: "",
          DefaultValue: "",
          groupMembers: []
        };
        this.errors = {};
        this.showModal = true;
      },

      // Edit test
      async editTest(id) {
        this.isEditing = true;
        this.errors = {};
        this.activeTab = 'basic';
        try {
          const res = await fetch(`${BASEURL}api/tests/${id}`, {
            credentials: 'include'
          });
          const data = await res.json();
          if (data.data) {
            const testData = data.data;
            // Map TypeCode to TestType (VID) for proper dialog display
            const typeCode = testData.TypeCode || '';
            let testTypeVid = testData.TestType || null;
            
            // Convert TypeCode string to VID if needed
            if (!testTypeVid && typeCode) {
              const typeMap = { 'TEST': 1, 'PARAM': 2, 'CALC': 3, 'GROUP': 4, 'TITLE': 5 };
              testTypeVid = typeMap[typeCode] || null;
            }
            
            this.form = {
              ...this.form,
              ...testData,
              TestType: testTypeVid,
              // Store TypeCode directly for dialog display
              TypeCode: typeCode,
              // Preserve group members if editing group
              groupMembers: testData.testdefgrp || []
            };
            this.showModal = true;
            this.showViewModal = false;
          }
        } catch (err) {
          console.error(err);
          alert('Failed to load test data');
        }
      },

      // Validate form
      validate() {
        const e = {};
        if (!this.form.TestSiteName?.trim()) e.TestSiteName = "Test name is required";
        if (!this.form.TestSiteCode?.trim()) e.TestSiteCode = "Test code is required";
        if (!this.form.TestType) e.TestType = "Test type is required";
        this.errors = e;
        return Object.keys(e).length === 0;
      },

      // Close modal
      closeModal() {
        this.showModal = false;
        this.showMemberSelector = false;
        this.errors = {};
        this.memberSearch = '';
      },

      // Toggle group member
      toggleMember(test) {
        if (!this.form.groupMembers) {
          this.form.groupMembers = [];
        }
        const index = this.form.groupMembers.findIndex(m => m.TestSiteID === test.TestSiteID);
        if (index > -1) {
          this.form.groupMembers.splice(index, 1);
        } else {
          this.form.groupMembers.push({
            TestSiteID: test.TestSiteID,
            TestSiteCode: test.TestSiteCode,
            TestSiteName: test.TestSiteName,
            MemberTypeCode: test.TypeCode || 'TEST',
            SeqScr: 0
          });
        }
        this.$forceUpdate();
      },

      // Remove group member
      removeMember(index) {
        this.form.groupMembers.splice(index, 1);
        this.$forceUpdate();
      },

      // Add common member quickly
      addCommonMember(code, type) {
        const test = this.availableTests.find(t => t.TestSiteCode === code);
        if (test) {
          if (!this.form.groupMembers) {
            this.form.groupMembers = [];
          }
          const exists = this.form.groupMembers.some(m => m.TestSiteID === test.TestSiteID);
          if (!exists) {
            this.form.groupMembers.push({
              TestSiteID: test.TestSiteID,
              TestSiteCode: test.TestSiteCode,
              TestSiteName: test.TestSiteName,
              MemberTypeCode: type,
              SeqScr: this.form.groupMembers.length + 1
            });
            this.$forceUpdate();
          }
        } else {
          // Add as custom entry if not found
          if (!this.form.groupMembers) {
            this.form.groupMembers = [];
          }
          this.form.groupMembers.push({
            TestSiteID: null,
            TestSiteCode: code,
            TestSiteName: code,
            MemberTypeCode: type,
            SeqScr: this.form.groupMembers.length + 1
          });
          this.$forceUpdate();
        }
      },

      // Save test
      async save() {
        if (!this.validate()) return;

        this.saving = true;
        try {
          const method = this.isEditing ? 'PATCH' : 'POST';
          const payload = { ...this.form };

          // Handle group members for GROUP type
          if (this.getTypeCode(this.form.TestType) === 'GROUP' && this.form.groupMembers?.length > 0) {
            payload.groupMembers = this.form.groupMembers;
          }

          const res = await fetch(`${BASEURL}api/tests`, {
            method: method,
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(payload),
            credentials: 'include'
          });

          const data = await res.json();

          if (res.ok) {
            this.closeModal();
            await this.fetchList();
          } else {
            alert(data.message || "Failed to save");
          }
        } catch (err) {
          console.error(err);
          alert("Failed to save test");
        } finally {
          this.saving = false;
        }
      },

      // Confirm delete
      confirmDelete(test) {
        this.deleteTarget = test;
        this.showDeleteModal = true;
      },

      // Delete test
      async deleteTest() {
        if (!this.deleteTarget) return;

        this.deleting = true;
        try {
          const res = await fetch(`${BASEURL}api/tests`, {
            method: 'DELETE',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ TestSiteID: this.deleteTarget.TestSiteID }),
            credentials: 'include'
          });

          if (res.ok) {
            this.showDeleteModal = false;
            await this.fetchList();
          } else {
            const data = await res.json();
            alert(data.message || "Failed to delete");
          }
        } catch (err) {
          console.error(err);
          alert("Failed to delete test");
        } finally {
          this.deleting = false;
          this.deleteTarget = null;
        }
      },

      // Get type code from VID (with fallback and null safety)
      getTypeCode(vid) {
        if (!vid) return '';
        // First try to find in loaded testTypes
        if (this.testTypes && Array.isArray(this.testTypes) && this.testTypes.length > 0) {
          const type = this.testTypes.find(t => t && t.VID === vid);
          if (type && type.VValue) return type.VValue;
        }
        // Fallback to hardcoded mapping
        const typeMap = { 1: 'TEST', 2: 'PARAM', 3: 'CALC', 4: 'GROUP', 5: 'TITLE' };
        return typeMap[vid] || '';
      }
    }
  }
</script>
<?= $this->endSection() ?>