<script>
  import { createEventDispatcher } from 'svelte';
  import { AlertCircle, Code, Loader2, RefreshCw, Save } from 'lucide-svelte';
import { compileRuleExpr, createRule, updateRule } from '$lib/api/rule.js';
  import { success as toastSuccess, error as toastError } from '$lib/utils/toast.js';
  import Modal from '$lib/components/Modal.svelte';
  import { resolve } from '$app/paths';

  let { open = $bindable(false), testSiteId = null, ruleMode = 'create', initialRule = null } = $props();
  const dispatch = createEventDispatcher();

  let ruleCode = $state('');
  let ruleName = $state('');
  let eventCode = $state('test_created');
  let rawExpr = $state('');

  let compiledExprJson = $state(null);
  let compiledExprObject = $state(null);
  let compileStatus = $state('idle');
  let compileError = $state(null);
  let lastCompiledRawExpr = $state(null);

  let isSaving = $state(false);
  let formErrors = $state({});
  let hasTriedSave = $state(false);
  let saveMessage = $state(null);

  $effect(() => {
    if (!open) return;
    initializeForm();
  });

  function initializeForm() {
    ruleCode = initialRule?.RuleCode || '';
    ruleName = initialRule?.RuleName || '';
    eventCode = initialRule?.EventCode || 'test_created';
    rawExpr = initialRule?.ConditionExpr || '';
    compiledExprJson = initialRule?.ConditionExprCompiled || null;
    compiledExprObject = parseCompiled(compiledExprJson);
    compileStatus = compiledExprJson ? 'success' : 'idle';
    lastCompiledRawExpr = compiledExprJson ? rawExpr : null;
    compileError = null;
    formErrors = {};
    hasTriedSave = false;
  }

  function parseCompiled(payload) {
    if (!payload) return null;
    try {
      return JSON.parse(payload);
    } catch {
      return null;
    }
  }

  function handleRawExprChange(value) {
    rawExpr = value;
    if (lastCompiledRawExpr !== null && lastCompiledRawExpr !== value) {
      compileStatus = 'stale';
    }
  }

  async function handleCompileClick() {
    if (!rawExpr.trim()) {
      compileError = 'Expression cannot be empty';
      compileStatus = 'error';
      return;
    }

    compileStatus = 'loading';
    compileError = null;

    try {
      const response = await compileRuleExpr(rawExpr);
      const data = response?.data?.data ?? response?.data ?? response;

      if (data?.conditionExprCompiled) {
        compiledExprJson = data.conditionExprCompiled;
        compiledExprObject = data.compiled ?? null;
        compileStatus = 'success';
        lastCompiledRawExpr = rawExpr;
      } else {
        throw new Error('Invalid compile response');
      }
    } catch (err) {
      compileError = err?.message || 'Compilation failed';
      compileStatus = 'error';
    }
  }

  function validateForm() {
    const errors = {};
    if (!ruleCode.trim()) errors.RuleCode = 'Rule code is required';
    if (!ruleName.trim()) errors.RuleName = 'Rule name is required';
    if (!eventCode.trim()) errors.EventCode = 'Event code is required';
    formErrors = errors;
    return Object.keys(errors).length === 0;
  }

  function isSaveDisabled() {
    if (isSaving) return true;
    if (!testSiteId) return true;
    if (!rawExpr.trim()) return false;
    if (compileStatus !== 'success') return true;
    if (lastCompiledRawExpr !== rawExpr) return true;
    return false;
  }

  function getSaveMessage() {
    if (!hasTriedSave) return null;
    if (!testSiteId) return 'Save the test first to link rules';
    if (isSaving) return 'Saving rule...';
    if (compileStatus === 'loading') return 'Compiling before save';
    if (compileStatus === 'error') return 'Fix compilation errors';
    if (compileStatus === 'stale') return 'Recompile before saving';
    if (!rawExpr.trim()) return null;
    return null;
  }

  async function handleSave() {
    hasTriedSave = true;
    if (!validateForm()) return;
    if (isSaveDisabled()) return;
    isSaving = true;

    const payload = {
      RuleCode: ruleCode.trim(),
      RuleName: ruleName.trim(),
      EventCode: eventCode,
      ConditionExpr: rawExpr.trim() || null,
      ConditionExprCompiled: rawExpr.trim() ? compiledExprJson : null
    };

    try {
      if (ruleMode === 'edit' && initialRule?.RuleID) {
        await updateRule(initialRule.RuleID, { ...payload, TestSiteIDs: [testSiteId] });
        toastSuccess('Rule updated');
      } else {
        await createRule({ ...payload, TestSiteIDs: [testSiteId] });
        toastSuccess('Rule created');
      }

      dispatch('saved');
      open = false;
    } catch (err) {
      const message = err?.message || 'Failed to save rule';
      toastError(message);
    } finally {
      isSaving = false;
    }
  }

  function handleCancel() {
    open = false;
    dispatch('close');
  }

  function getStatusBadge() {
    switch (compileStatus) {
      case 'loading':
        return { text: 'Compiling', class: 'badge-info' };
      case 'success':
        return { text: 'Compiled', class: 'badge-success' };
      case 'stale':
        return { text: 'Stale', class: 'badge-warning' };
      case 'error':
        return { text: 'Error', class: 'badge-error' };
      default:
        return { text: 'Not compiled', class: 'badge-ghost' };
    }
  }

  const statusBadge = $derived(getStatusBadge);
  const saveDisabled = $derived(isSaveDisabled);

  $effect(() => {
    saveMessage = getSaveMessage();
  });
</script>

<Modal
  bind:open
  size="lg"
  title={ruleMode === 'edit' ? 'Edit Rule' : 'New Rule'}
>
  {#if !testSiteId}
    <div class="alert alert-warning text-sm">
      <AlertCircle class="w-4 h-4" />
      Save the test first, so we can link rules by `TestSiteID`.
    </div>
  {:else}
    <div class="space-y-4">
      <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
        <div class="space-y-1">
          <label for="ruleCode" class="label-text text-sm font-medium">Rule Code</label>
          <div class="input input-bordered input-sm flex items-center gap-2">
            <Code class="w-4 h-4 text-gray-400" />
            <input
              id="ruleCode"
              type="text"
              class="grow bg-transparent outline-none"
              value={ruleCode}
              oninput={(event) => (ruleCode = event.currentTarget.value)}
              disabled={isSaving}
            />
          </div>
          {#if formErrors.RuleCode}
            <p class="text-xs text-error">{formErrors.RuleCode}</p>
          {/if}
        </div>
        <div class="space-y-1">
          <label for="ruleName" class="label-text text-sm font-medium">Rule Name</label>
          <div class="input input-bordered input-sm flex items-center gap-2">
            <Code class="w-4 h-4 text-gray-400" />
            <input
              id="ruleName"
              type="text"
              class="grow bg-transparent outline-none"
              value={ruleName}
              oninput={(event) => (ruleName = event.currentTarget.value)}
              disabled={isSaving}
            />
          </div>
          {#if formErrors.RuleName}
            <p class="text-xs text-error">{formErrors.RuleName}</p>
          {/if}
        </div>
      </div>

      <div>
        <label for="eventCode" class="label-text text-sm font-medium">Event Code</label>
        <select
          id="eventCode"
          class="select select-sm select-bordered w-full"
          bind:value={eventCode}
          disabled={isSaving}
        >
          <option value="test_created">test_created</option>
          <option value="result_updated">result_updated</option>
          <option value="ORDER_CREATED">ORDER_CREATED</option>
        </select>
        {#if formErrors.EventCode}
          <p class="text-xs text-error">{formErrors.EventCode}</p>
        {/if}
      </div>

      <div class="space-y-1">
        <div class="flex items-center justify-between">
          <label for="conditionExpr" class="label-text text-sm font-medium">Condition Expression</label>
          <span class="badge badge-sm {statusBadge.class}">{statusBadge.text}</span>
        </div>
        <textarea
          id="conditionExpr"
          rows="4"
          class="textarea textarea-bordered w-full font-mono text-sm"
          value={rawExpr}
          oninput={(event) => handleRawExprChange(event.currentTarget.value)}
          placeholder="if(sex('M'); result_set('HGB', 14); nothing)"
          disabled={isSaving}
        ></textarea>
        <div class="flex items-center justify-between">
          <small class="text-xs text-gray-500">Use the DSL syntax described in <a href={resolve('/docs/test-rule-engine.md')} class="text-primary underline">docs</a>.</small>
          <button
            class="btn btn-sm btn-primary"
            type="button"
            onclick={handleCompileClick}
            disabled={isSaving || compileStatus === 'loading' || !rawExpr.trim()}
          >
            {#if compileStatus === 'loading'}
              <Loader2 class="w-3 h-3 animate-spin mr-1" />
              Compiling...
            {:else}
              <RefreshCw class="w-3 h-3 mr-1" />
              Compile
            {/if}
          </button>
        </div>
        {#if compileError}
          <div class="alert alert-error alert-sm">
            <AlertCircle class="w-4 h-4" />
            <span class="text-xs">{compileError}</span>
          </div>
        {/if}
      </div>

      {#if compiledExprObject && compileStatus === 'success'}
        <div class="bg-base-100 border border-base-200 rounded-lg p-3 text-xs text-gray-600">
          <p class="font-semibold text-gray-700 mb-1">Compiled preview</p>
          <pre class="font-mono text-[11px] overflow-auto max-h-40">{JSON.stringify(compiledExprObject, null, 2)}</pre>
        </div>
      {/if}
    </div>
  {/if}

  {#snippet footer()}
    <button class="btn btn-ghost" type="button" onclick={handleCancel} disabled={isSaving}>Cancel</button>
    <button
      class="btn btn-primary"
      type="button"
      onclick={handleSave}
      disabled={saveDisabled}
    >
      {#if isSaving}
        <Loader2 class="w-4 h-4 mr-2 animate-spin" />
        Saving...
      {:else}
        <Save class="w-4 h-4 mr-2" />
        {ruleMode === 'edit' ? 'Update Rule' : 'Create Rule'}
      {/if}
    </button>
  {/snippet}

  {#if saveMessage}
    <div class="text-xs text-warning mt-2">{saveMessage}</div>
  {/if}
</Modal>
