<!-- [DEF:MigrationDashboard:Component] -->
<!--
@SEMANTICS: migration, dashboard, environment, selection, database-replacement
@PURPOSE:   Main dashboard for configuring and starting migrations.
@LAYER:     Page
@RELATION:  USES -> EnvSelector

@INVARIANT: Migration cannot start without source and target environments.
-->

<script lang="ts">
  // [SECTION: IMPORTS]
  import { onMount } from 'svelte';
  import EnvSelector from '../../components/EnvSelector.svelte';
  import DashboardGrid from '../../components/DashboardGrid.svelte';
  import MappingTable from '../../components/MappingTable.svelte';
  import TaskRunner from '../../components/TaskRunner.svelte';
  import TaskHistory from '../../components/TaskHistory.svelte';
  import TaskLogViewer from '../../components/TaskLogViewer.svelte';
  import PasswordPrompt from '../../components/PasswordPrompt.svelte';
  import { selectedTask } from '../../lib/stores.js';
  import { resumeTask } from '../../services/taskService.js';
  import type { DashboardMetadata, DashboardSelection } from '../../types/dashboard';
  // [/SECTION]

  // [SECTION: STATE]
  let environments: any[] = [];
  let sourceEnvId = "";
  let targetEnvId = "";
  let replaceDb = false;
  let loading = true;
  let error = "";
  let dashboards: DashboardMetadata[] = [];
  let selectedDashboardIds: number[] = [];
  let sourceDatabases: any[] = [];
  let targetDatabases: any[] = [];
  let mappings: any[] = [];
  let suggestions: any[] = [];
  let fetchingDbs = false;
  
  // UI State for Modals
  let showLogViewer = false;
  let logViewerTaskId: string | null = null;
  let logViewerTaskStatus: string | null = null;
  
  let showPasswordPrompt = false;
  let passwordPromptDatabases: string[] = [];
  let passwordPromptErrorMessage = "";
  // [/SECTION]

  // [DEF:fetchEnvironments:Function]
  /**
   * @purpose Fetches the list of environments from the API.
   * @pre     None.
   * @post    environments state is updated.
   */
  async function fetchEnvironments() {
    try {
      const response = await fetch('/api/environments');
      if (!response.ok) throw new Error('Failed to fetch environments');
      environments = await response.json();
    } catch (e) {
      error = e.message;
    } finally {
      loading = false;
    }
  }
  // [/DEF:fetchEnvironments:Function]

  // [DEF:fetchDashboards:Function]
  /**
   * @purpose Fetches dashboards for the selected source environment.
   * @pre     envId is a valid environment ID.
   * @param envId The environment ID.
   * @post    dashboards state is updated.
   */
  async function fetchDashboards(envId: string) {
    try {
      const response = await fetch(`/api/environments/${envId}/dashboards`);
      if (!response.ok) throw new Error('Failed to fetch dashboards');
      dashboards = await response.json();
      selectedDashboardIds = []; // Reset selection when env changes
    } catch (e) {
      error = e.message;
      dashboards = [];
    }
  }
  // [/DEF:fetchDashboards:Function]

  onMount(fetchEnvironments);

  // Reactive: fetch dashboards when source env changes
  $: if (sourceEnvId) fetchDashboards(sourceEnvId);

  // [DEF:fetchDatabases:Function]
  /**
   * @purpose Fetches databases from both environments and gets suggestions.
   * @pre     sourceEnvId and targetEnvId must be set.
   * @post    sourceDatabases, targetDatabases, mappings, and suggestions are updated.
   */
  async function fetchDatabases() {
    if (!sourceEnvId || !targetEnvId) return;
    fetchingDbs = true;
    error = "";
    
    try {
      const [srcRes, tgtRes, mapRes, sugRes] = await Promise.all([
        fetch(`/api/environments/${sourceEnvId}/databases`),
        fetch(`/api/environments/${targetEnvId}/databases`),
        fetch(`/api/mappings?source_env_id=${sourceEnvId}&target_env_id=${targetEnvId}`),
        fetch(`/api/mappings/suggest`, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ source_env_id: sourceEnvId, target_env_id: targetEnvId })
        })
      ]);

      if (!srcRes.ok || !tgtRes.ok) throw new Error('Failed to fetch databases from environments');
      
      sourceDatabases = await srcRes.json();
      targetDatabases = await tgtRes.json();
      mappings = await mapRes.json();
      suggestions = await sugRes.json();
    } catch (e) {
      error = e.message;
    } finally {
      fetchingDbs = false;
    }
  }
  // [/DEF:fetchDatabases:Function]

  // [DEF:handleMappingUpdate:Function]
  /**
   * @purpose Saves a mapping to the backend.
   * @pre     event.detail contains sourceUuid and targetUuid.
   * @post    Mapping is saved and local mappings list is updated.
   */
  async function handleMappingUpdate(event: CustomEvent) {
    const { sourceUuid, targetUuid } = event.detail;
    const sDb = sourceDatabases.find(d => d.uuid === sourceUuid);
    const tDb = targetDatabases.find(d => d.uuid === targetUuid);
    
    if (!sDb || !tDb) return;

    try {
      const response = await fetch('/api/mappings', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
          source_env_id: sourceEnvId,
          target_env_id: targetEnvId,
          source_db_uuid: sourceUuid,
          target_db_uuid: targetUuid,
          source_db_name: sDb.database_name,
          target_db_name: tDb.database_name
        })
      });

      if (!response.ok) throw new Error('Failed to save mapping');
      
      const savedMapping = await response.json();
      mappings = [...mappings.filter(m => m.source_db_uuid !== sourceUuid), savedMapping];
    } catch (e) {
      error = e.message;
    }
  }
  // [/DEF:handleMappingUpdate:Function]

  // [DEF:handleViewLogs:Function]
  // @PURPOSE: Opens the log viewer for a specific task.
  // @PRE: event.detail contains task object.
  // @POST: logViewer state updated and showLogViewer set to true.
  function handleViewLogs(event: CustomEvent) {
      const task = event.detail;
      logViewerTaskId = task.id;
      logViewerTaskStatus = task.status;
      showLogViewer = true;
  }
  // [/DEF:handleViewLogs:Function]

  // [DEF:handlePasswordPrompt:Function]
  // @PURPOSE: Reactive logic to show password prompt when a task is awaiting input.
  // @PRE: selectedTask status is AWAITING_INPUT.
  // @POST: showPasswordPrompt set to true with request data.
  // This is triggered by TaskRunner or TaskHistory when a task needs input
  // For now, we rely on the WebSocket or manual check.
  // Ideally, TaskHistory or TaskRunner emits an event when input is needed.
  // Or we watch selectedTask.
  
  $: if ($selectedTask && $selectedTask.status === 'AWAITING_INPUT' && $selectedTask.input_request) {
      const req = $selectedTask.input_request;
      if (req.type === 'database_password') {
          passwordPromptDatabases = req.databases || [];
          passwordPromptErrorMessage = req.error_message || "";
          showPasswordPrompt = true;
      }
  } else if (!$selectedTask || $selectedTask.status !== 'AWAITING_INPUT') {
      // Close prompt if task is no longer waiting (e.g. resumed)
      // But only if we are viewing this task.
      // showPasswordPrompt = false;
      // Actually, don't auto-close, let the user or success handler close it.
  }
  // [/DEF:handlePasswordPrompt:Function]

  // [DEF:handleResumeMigration:Function]
  // @PURPOSE: Resumes a migration task with provided passwords.
  // @PRE: event.detail contains passwords.
  // @POST: resumeTask is called and showPasswordPrompt is hidden on success.
  async function handleResumeMigration(event: CustomEvent) {
      if (!$selectedTask) return;
      
      const { passwords } = event.detail;
      try {
          await resumeTask($selectedTask.id, passwords);
          showPasswordPrompt = false;
          // Task status update will be handled by store/websocket
      } catch (e) {
          console.error("Failed to resume task:", e);
          passwordPromptErrorMessage = e.message;
          // Keep prompt open
      }
  }
  // [/DEF:handleResumeMigration:Function]

  // [DEF:startMigration:Function]
  /**
   * @purpose Starts the migration process.
   * @pre     sourceEnvId and targetEnvId must be set and different.
   * @post    Migration task is started and selectedTask is updated.
   */
  async function startMigration() {
    if (!sourceEnvId || !targetEnvId) {
      error = "Please select both source and target environments.";
      return;
    }
    if (sourceEnvId === targetEnvId) {
      error = "Source and target environments must be different.";
      return;
    }
    if (selectedDashboardIds.length === 0) {
      error = "Please select at least one dashboard to migrate.";
      return;
    }

    error = "";
    try {
      const selection: DashboardSelection = {
        selected_ids: selectedDashboardIds,
        source_env_id: sourceEnvId,
        target_env_id: targetEnvId,
        replace_db_config: replaceDb
      };
      console.log(`[MigrationDashboard][Action] Starting migration with selection:`, selection);
      const response = await fetch('/api/migration/execute', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(selection)
      });
      console.log(`[MigrationDashboard][Action] API response status: ${response.status}`);
      if (!response.ok) throw new Error(`Failed to start migration: ${response.status} ${response.statusText}`);
      const result = await response.json();
      console.log(`[MigrationDashboard][Action] Migration started: ${result.task_id} - ${result.message}`);
      
      // Wait a brief moment for the backend to ensure the task is retrievable
      await new Promise(r => setTimeout(r, 500));
      
      // Fetch full task details and switch to TaskRunner view
      try {
        const taskRes = await fetch(`/api/tasks/${result.task_id}`);
        if (taskRes.ok) {
          const task = await taskRes.json();
          selectedTask.set(task);
        } else {
            // Fallback: create a temporary task object to switch view immediately
            console.warn("Could not fetch task details immediately, using placeholder.");
            selectedTask.set({
                id: result.task_id,
                plugin_id: 'superset-migration',
                status: 'RUNNING',
                logs: [],
                params: {}
            });
        }
      } catch (fetchErr) {
        console.error("Failed to fetch new task details:", fetchErr);
      }
    } catch (e) {
      console.error(`[MigrationDashboard][Failure] Migration failed:`, e);
      error = e.message;
    }
  }
  // [/DEF:startMigration:Function]
</script>

<!-- [SECTION: TEMPLATE] -->
<div class="max-w-4xl mx-auto p-6">
  <h1 class="text-2xl font-bold mb-6">Migration Dashboard</h1>
  
  <TaskHistory on:viewLogs={handleViewLogs} />

  {#if $selectedTask}
    <div class="mt-6">
      <TaskRunner />
      <button
        on:click={() => selectedTask.set(null)}
        class="mt-4 inline-flex items-center px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
      >
        Back to New Migration
      </button>
    </div>
  {:else}
    {#if loading}
      <p>Loading environments...</p>
    {:else if error}
      <div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-4">
        {error}
      </div>
    {/if}

    <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8">
      <EnvSelector
        label="Source Environment"
        bind:selectedId={sourceEnvId}
        {environments}
      />
      <EnvSelector
        label="Target Environment"
        bind:selectedId={targetEnvId}
        {environments}
      />
    </div>

    <!-- [DEF:DashboardSelectionSection] -->
    <div class="mb-8">
      <h2 class="text-lg font-medium mb-4">Select Dashboards</h2>
      
      {#if sourceEnvId}
        <DashboardGrid
          {dashboards}
          bind:selectedIds={selectedDashboardIds}
        />
      {:else}
        <p class="text-gray-500 italic">Select a source environment to view dashboards.</p>
      {/if}
    </div>
    <!-- [/DEF:DashboardSelectionSection] -->


    <div class="flex items-center mb-4">
      <input
        id="replace-db"
        type="checkbox"
        bind:checked={replaceDb}
        on:change={() => { if (replaceDb && sourceDatabases.length === 0) fetchDatabases(); }}
        class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
      />
      <label for="replace-db" class="ml-2 block text-sm text-gray-900">
        Replace Database (Apply Mappings)
      </label>
    </div>

    {#if replaceDb}
      <div class="mb-8 p-4 border rounded-md bg-gray-50">
        <h3 class="text-md font-medium mb-4">Database Mappings</h3>
        {#if fetchingDbs}
          <p>Loading databases and suggestions...</p>
        {:else if sourceDatabases.length > 0}
          <MappingTable
            {sourceDatabases}
            {targetDatabases}
            {mappings}
            {suggestions}
            on:update={handleMappingUpdate}
          />
        {:else if sourceEnvId && targetEnvId}
          <button
            on:click={fetchDatabases}
            class="text-indigo-600 hover:text-indigo-500 text-sm font-medium"
          >
            Refresh Databases & Suggestions
          </button>
        {/if}
      </div>
    {/if}

    <button
      on:click={startMigration}
      disabled={!sourceEnvId || !targetEnvId || sourceEnvId === targetEnvId || selectedDashboardIds.length === 0}
      class="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md shadow-sm text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500 disabled:bg-gray-400"
    >
      Start Migration
    </button>
  {/if}
</div>

<!-- Modals -->
<TaskLogViewer
    bind:show={showLogViewer}
    taskId={logViewerTaskId}
    taskStatus={logViewerTaskStatus}
    on:close={() => showLogViewer = false}
/>

<PasswordPrompt
    bind:show={showPasswordPrompt}
    databases={passwordPromptDatabases}
    errorMessage={passwordPromptErrorMessage}
    on:resume={handleResumeMigration}
    on:cancel={() => showPasswordPrompt = false}
/>

<!-- [/SECTION] -->

<style>
  /* Page specific styles */
</style>

<!-- [/DEF:MigrationDashboard:Component] -->
