<script lang="ts">
  import Screen from "./Screen.svelte";
  import type { CompleteWorkflowNavigateArgs } from "../../types/components/button";
  import { PaywallStateStore, setPaywallStateContext } from "../../stores/state";
  import type { ColorScheme } from "../../types/colors";
  import type { InitialInputSelections } from "../../stores/inputValidation";
  import type { OnComponentInteraction } from "../../types/paywall-component-interaction";
  import type {
    PackageInfo,
    VariableDictionary,
    CustomVariables,
  } from "../../types/variables";
  import type {
    WalletButtonRender,
    WalletPurchasePrepare,
    WalletPurchaseStart,
  } from "../../types/wallet";
  import type { UIConfig } from "../../types/ui-config";
  import type { ReservedAttribute } from "../../types/components/input-text";
  import type {
    WorkflowNavData,
    WorkflowStepChangeEvent,
  } from "../../types/workflow-nav";
  import type { StateDeclaration } from "../../types/state";
  import type {
    WorkflowStepTriggerAction,
    WorkflowStep,
  } from "../../types/workflow";
  import type {
    PaywallOffering,
    PaywallPackage,
  } from "@revenuecat/workflow-web-components-sdk";
  import type { BridgeInputs } from "../web-view/build-paywall-context";

  type TriggerActionValue = WorkflowStep["trigger_actions"][string];

  function isStepTriggerAction(
    a: TriggerActionValue,
  ): a is WorkflowStepTriggerAction {
    return (
      a.type === "step" &&
      typeof (a as WorkflowStepTriggerAction).step_id === "string"
    );
  }

  interface Props {
    workflow: WorkflowNavData;
    uiConfig: UIConfig;
    selectedLocale?: string;
    globalVariables?: VariableDictionary;
    variablesPerPackage?: Record<string, VariableDictionary>;
    /** Trial/intro/promo flags per package; required for intro_offer overrides. */
    infoPerPackage?: Record<string, PackageInfo>;
    maxContentWidth?: string;
    initialInputSelections?: InitialInputSelections;
    containerId?: string;
    walletButtonRender?: WalletButtonRender;
    prepareWalletPurchase?: WalletPurchasePrepare;
    onWalletPurchaseClicked?: WalletPurchaseStart;
    appUserId?: string;
    isSandbox?: boolean;
    rcSource?: string;
    onCustomWebCheckout?: (url: string) => void;
    safeAreaFallbackColor?: ColorScheme | null;
    hideBackButtons?: boolean;
    onPurchaseClicked?: (
      packageId: string,
      actionId: string,
    ) => void | Promise<void>;
    onActionTriggered?: (actionId: string) => void;
    onInputChanged?: (
      fieldId: string,
      value: string,
      actionId?: string,
    ) => void;
    /**
     * Called for sensitive inputs (keyboard_type: "password") instead of
     * onInputChanged, so hosts can keep the value in memory only.
     */
    onSensitiveInputChanged?: (fieldId: string, value: string) => void;
    onReservedAttributeChanged?: (
      reservedAttribute: ReservedAttribute,
      value: string,
    ) => void;
    onCompleteWorkflowNavigate?: (
      args: CompleteWorkflowNavigateArgs,
    ) => void | Promise<void>;
    onNavigateToUrlClicked?: (url: string) => void;
    onRestorePurchasesClicked?: () => void;
    onVisitCustomerCenterClicked?: () => void;
    onCookieSettingsClicked?: () => void;
    onComponentInteraction?: OnComponentInteraction;
    onClose?: () => void;
    onExitBack?: () => void;
    /**
     * Called whenever the current workflow step changes — on initial load,
     * forward navigation, back navigation, and when leaving a step (close,
     * purchase, error). Used by the host to fire workflow lifecycle events.
     */
    onStepChanged?: (event: WorkflowStepChangeEvent) => void;
    offering?: PaywallOffering | null;
    packages?: PaywallPackage[];
    isPreview?: boolean;
    inputs?: BridgeInputs;
    customVariables?: CustomVariables;
  }

  const {
    workflow,
    uiConfig,
    selectedLocale,
    globalVariables,
    variablesPerPackage,
    infoPerPackage,
    maxContentWidth,
    initialInputSelections,
    containerId,
    walletButtonRender,
    prepareWalletPurchase,
    onWalletPurchaseClicked,
    appUserId,
    isSandbox,
    rcSource,
    onCustomWebCheckout,
    safeAreaFallbackColor,
    hideBackButtons,
    onPurchaseClicked,
    onActionTriggered,
    onInputChanged,
    onSensitiveInputChanged,
    onReservedAttributeChanged,
    onCompleteWorkflowNavigate,
    onNavigateToUrlClicked,
    onRestorePurchasesClicked,
    onVisitCustomerCenterClicked,
    onCookieSettingsClicked,
    onComponentInteraction,
    onClose,
    onExitBack,
    onStepChanged,
    offering,
    packages,
    isPreview,
    inputs,
    customVariables,
  }: Props = $props();

  // ── State store ────────────────────────────────────────────────────────────
  // Created above the {#key currentPageId} block so it survives screen navigation.
  // Seeded upfront by merging declarations from all screens; each Screen's Paywall
  // calls registerDeclarations() defensively (idempotent, non-overwriting).
  const workflowStateStore = new PaywallStateStore();
  setPaywallStateContext(workflowStateStore);

  function mergedStateDeclarations(
    pages: WorkflowNavData["pages"],
  ): Record<string, StateDeclaration> {
    const merged: Record<string, StateDeclaration> = {};
    for (const screen of Object.values(pages)) {
      for (const [key, declaration] of Object.entries(
        screen.state_declarations ?? {},
      )) {
        if (!Object.prototype.hasOwnProperty.call(merged, key))
          merged[key] = declaration;
      }
    }
    return merged;
  }

  let trackedWorkflowId = $state<string>();
  $effect.pre(() => {
    if (trackedWorkflowId !== workflow.workflowId) {
      trackedWorkflowId = workflow.workflowId;
      workflowStateStore.reset(mergedStateDeclarations(workflow.pages));
    }
  });

  // ── Nav stack ─────────────────────────────────────────────────────────────
  // Each entry pairs the screen ID (used to look up the page to render) with
  // the step ID (used to resolve trigger_actions on button press). The current
  // page is always the last entry; workflow actions push, navigate_back pops.
  //
  // We key the reset off workflowId + initial_page_id so a swapped workflow
  // cannot leave stale navigation when the entry page id happens to match.
  interface NavEntry {
    pageId: string;
    stepId: string;
  }
  let navStack = $state<NavEntry[]>([]);
  let trackedNavKey = $state<string>();

  const current = $derived(
    navStack[navStack.length - 1] ?? {
      pageId: workflow.initial_page_id,
      stepId: workflow.initial_step_id,
    },
  );
  const currentScreen = $derived(workflow.pages[current.pageId]);
  const currentStep = $derived(workflow.steps[current.stepId]);
  const bridgeWorkflow = $derived({
    workflow_id: workflow.workflowId,
    step_id: current.stepId,
    step_type: currentStep?.type ?? "screen",
    screen_type: currentStep?.screen_type ?? [],
  });

  $effect.pre(() => {
    const navKey = `${workflow.workflowId}:${workflow.initial_page_id}`;
    if (trackedNavKey !== navKey) {
      trackedNavKey = navKey;
      navStack = [
        {
          pageId: workflow.initial_page_id,
          stepId: workflow.initial_step_id,
        },
      ];
      emitStepChange({
        workflowId: workflow.workflowId,
        stepId: workflow.initial_step_id,
        fromStepId: undefined,
        toStepId: undefined,
        reason: "start",
        ...stepFlags(workflow.initial_step_id),
      });
    }
  });

  function stepFlags(stepId: string): {
    isFirstStep: boolean;
    isLastStep: boolean;
  } {
    const step = workflow.steps[stepId];
    return {
      isFirstStep: stepId === workflow.initial_step_id,
      // A step is "last" when it has no step-type trigger actions — i.e. no
      // client-resolvable forward navigation. Condition-based actions fall back
      // to onActionTriggered and are not counted here.
      isLastStep:
        step !== undefined &&
        !Object.values(step.trigger_actions).some((a) =>
          isStepTriggerAction(a),
        ),
    };
  }

  function emitStepChange(event: WorkflowStepChangeEvent) {
    onStepChanged?.(event);
  }

  /**
   * Resolves a "workflow"-type button action and navigates to the next screen.
   *
   * Flow: component ID → step.triggers[].action_id → trigger_actions[action_id]
   *       → step_id → step.screen_id → push onto nav stack.
   */
  function handleActionTriggered(actionId: string) {
    const currentStep = workflow.steps[current.stepId];
    if (!currentStep) {
      console.warn(
        `[Workflow] onActionTriggered: no current step found for step_id "${current.stepId}"`,
      );
      return;
    }

    // Resolve the component ID to an action ID via the step's triggers array.
    const resolvedActionId = currentStep.triggers.find(
      (t) => t.component_id === actionId,
    )?.action_id;

    if (!resolvedActionId) {
      console.warn(
        `[Workflow] onActionTriggered: no trigger entry found for component "${actionId}" on step "${current.stepId}"`,
      );
      onActionTriggered?.(actionId);
      return;
    }

    const triggerAction = currentStep.trigger_actions[resolvedActionId];
    if (!triggerAction) {
      console.warn(
        `[Workflow] onActionTriggered: no trigger_action found for action_id "${resolvedActionId}" on step "${current.stepId}"`,
      );
      onActionTriggered?.(actionId);
      return;
    }

    if (!isStepTriggerAction(triggerAction)) {
      // Condition-based actions require a full workflow engine — fall back to
      // the host's onActionTriggered for anything we can't resolve client-side.
      console.warn(
        `[Workflow] onActionTriggered: trigger_action is not a simple step action, falling back to onActionTriggered`,
      );
      onActionTriggered?.(actionId);
      return;
    }

    const nextStep = workflow.steps[triggerAction.step_id];
    if (!nextStep) {
      console.warn(
        `[Workflow] onActionTriggered: step_id "${triggerAction.step_id}" not found in workflow steps`,
      );
      return;
    }

    const nextPageId = nextStep.screen_id;
    if (!nextPageId) {
      console.warn(
        `[Workflow] onActionTriggered: step "${triggerAction.step_id}" has no screen_id (logic/experiment step?)`,
      );
      return;
    }

    if (!workflow.pages[nextPageId]) {
      console.warn(
        `[Workflow] onActionTriggered: screen_id "${nextPageId}" not found in workflow pages`,
      );
      return;
    }

    const fromStepId = current.stepId;
    const toStepId = triggerAction.step_id;
    emitStepChange({
      workflowId: workflow.workflowId,
      stepId: fromStepId,
      fromStepId: undefined,
      toStepId,
      reason: "completed",
      ...stepFlags(fromStepId),
    });
    navStack = [...navStack, { pageId: nextPageId, stepId: toStepId }];
    emitStepChange({
      workflowId: workflow.workflowId,
      stepId: toStepId,
      fromStepId,
      toStepId: undefined,
      reason: "forward",
      ...stepFlags(toStepId),
    });
  }

  function handleBackClicked() {
    if (navStack.length > 1) {
      const fromStepId = current.stepId;
      const toStepId = navStack[navStack.length - 2].stepId;
      emitStepChange({
        workflowId: workflow.workflowId,
        stepId: fromStepId,
        fromStepId: undefined,
        toStepId,
        reason: "completed",
        ...stepFlags(fromStepId),
      });
      navStack = navStack.slice(0, -1);
      emitStepChange({
        workflowId: workflow.workflowId,
        stepId: toStepId,
        fromStepId,
        toStepId: undefined,
        reason: "back",
        ...stepFlags(toStepId),
      });
    } else {
      // Already at root — bubble up to the host.
      emitStepChange({
        workflowId: workflow.workflowId,
        stepId: current.stepId,
        fromStepId: undefined,
        toStepId: undefined,
        reason: "completed",
        ...stepFlags(current.stepId),
      });
      onExitBack?.();
    }
  }
</script>

{#key current.pageId}
  <Screen
    paywallComponents={currentScreen}
    {uiConfig}
    {selectedLocale}
    {globalVariables}
    {variablesPerPackage}
    {infoPerPackage}
    {maxContentWidth}
    {initialInputSelections}
    {containerId}
    {walletButtonRender}
    {prepareWalletPurchase}
    {onWalletPurchaseClicked}
    {appUserId}
    {isSandbox}
    {rcSource}
    {onCustomWebCheckout}
    {safeAreaFallbackColor}
    {hideBackButtons}
    {onPurchaseClicked}
    onActionTriggered={handleActionTriggered}
    {onInputChanged}
    {onSensitiveInputChanged}
    {onReservedAttributeChanged}
    {onCompleteWorkflowNavigate}
    {onNavigateToUrlClicked}
    {onRestorePurchasesClicked}
    {onVisitCustomerCenterClicked}
    {onCookieSettingsClicked}
    {onComponentInteraction}
    onBackClicked={handleBackClicked}
    onClose={() => {
      emitStepChange({
        workflowId: workflow.workflowId,
        stepId: current.stepId,
        fromStepId: undefined,
        toStepId: undefined,
        reason: "completed",
        ...stepFlags(current.stepId),
      });
      onClose?.();
    }}
    {offering}
    {packages}
    workflow={bridgeWorkflow}
    {isPreview}
    {inputs}
    {customVariables}
  />
{/key}
