import { createClient, type SupabaseClient } from "@supabase/supabase-js";
import { scenarios } from "../lib/scenarios";
import type { ScenarioDefinition } from "../lib/types";

type ScenarioTemplateRow = {
  id: string;
  slug: string;
  current_published_version_id: string | null;
  current_draft_version_id: string | null;
};

type ScenarioVersionRow = {
  id: string;
  version_number: number;
  status: "draft" | "published" | "archived";
  published_at: string | null;
};

type ClientRow = {
  id: string;
};

type TrainingSessionBackfillRow = {
  id: string;
  client_id: string | null;
  scenario_id: string;
  scenario_slug: string | null;
  scenario_template_id: string | null;
  scenario_version_id: string | null;
  scenario_snapshot: unknown;
};

function getRequiredEnv(name: string) {
  const value = process.env[name];

  if (!value) {
    throw new Error(`Missing required environment variable: ${name}`);
  }

  return value;
}

function toVersionPayload(scenario: ScenarioDefinition) {
  return {
    id: scenario.id,
    title: scenario.title,
    targetSkill: scenario.targetSkill,
    difficulty: scenario.difficulty,
    durationLabel: scenario.durationLabel,
    shortBrief: scenario.shortBrief,
    employeeProfile: scenario.employeeProfile,
    openingMessage: scenario.openingMessage,
    managerGoal: scenario.managerGoal,
    successSignals: scenario.successSignals,
    failureSignals: scenario.failureSignals,
    completionRules: scenario.completionRules,
    metricWeights: scenario.metricWeights,
    maxTurns: scenario.maxTurns,
    tensionColor: scenario.tensionColor,
  };
}

async function ensurePublishedScenario(
  supabase: SupabaseClient,
  scenario: ScenarioDefinition,
) {
  const { data: template, error: templateError } = await supabase
    .from("scenario_templates")
    .upsert(
      {
        slug: scenario.id,
      },
      {
        onConflict: "slug",
      },
    )
    .select("id, slug, current_published_version_id, current_draft_version_id")
    .single();

  if (templateError) {
    throw templateError;
  }

  const templateRow = template as ScenarioTemplateRow;

  const { data: versions, error: versionsError } = await supabase
    .from("scenario_versions")
    .select("id, version_number, status, published_at")
    .eq("template_id", templateRow.id)
    .order("version_number", { ascending: false });

  if (versionsError) {
    throw versionsError;
  }

  const existingVersions = (versions ?? []) as ScenarioVersionRow[];
  const currentPublished =
    existingVersions.find(
      (version) => version.id === templateRow.current_published_version_id,
    ) ?? existingVersions.find((version) => version.status === "published");

  let versionId = currentPublished?.id ?? null;

  if (versionId) {
    const { error: updateVersionError } = await supabase
      .from("scenario_versions")
      .update({
        status: "published",
        payload: toVersionPayload(scenario),
        title: scenario.title,
        target_skill: scenario.targetSkill,
        difficulty: scenario.difficulty,
        duration_label: scenario.durationLabel,
        short_brief: scenario.shortBrief,
        published_at: currentPublished?.published_at ?? new Date().toISOString(),
      })
      .eq("id", versionId);

    if (updateVersionError) {
      throw updateVersionError;
    }
  } else {
    const nextVersionNumber = (existingVersions[0]?.version_number ?? 0) + 1;
    const { data: createdVersion, error: createVersionError } = await supabase
      .from("scenario_versions")
      .insert({
        template_id: templateRow.id,
        version_number: nextVersionNumber,
        status: "published",
        payload: toVersionPayload(scenario),
        title: scenario.title,
        target_skill: scenario.targetSkill,
        difficulty: scenario.difficulty,
        duration_label: scenario.durationLabel,
        short_brief: scenario.shortBrief,
        published_at: new Date().toISOString(),
      })
      .select("id")
      .single();

    if (createVersionError) {
      throw createVersionError;
    }

    versionId = createdVersion.id as string;
  }

  const { error: updateTemplateError } = await supabase
    .from("scenario_templates")
    .update({
      status: "published",
      current_published_version_id: versionId,
    })
    .eq("id", templateRow.id);

  if (updateTemplateError) {
    throw updateTemplateError;
  }

  return {
    templateId: templateRow.id,
    versionId,
  };
}

async function backfillLearnerAccess(
  supabase: SupabaseClient,
  templateIds: string[],
) {
  const { data: clients, error: clientsError } = await supabase
    .from("clients")
    .select("id")
    .eq("status", "active");

  if (clientsError) {
    throw clientsError;
  }

  for (const client of (clients ?? []) as ClientRow[]) {
    const [{ count: directCount, error: directError }, { count: groupCount, error: groupError }] =
      await Promise.all([
        supabase
          .from("client_scenario_access")
          .select("scenario_template_id", { count: "exact", head: true })
          .eq("client_id", client.id),
        supabase
          .from("client_group_access")
          .select("group_id", { count: "exact", head: true })
          .eq("client_id", client.id),
      ]);

    if (directError) {
      throw directError;
    }

    if (groupError) {
      throw groupError;
    }

    if ((directCount ?? 0) > 0 || (groupCount ?? 0) > 0) {
      continue;
    }

    const { error: insertAccessError } = await supabase
      .from("client_scenario_access")
      .upsert(
        templateIds.map((templateId) => ({
          client_id: client.id,
          scenario_template_id: templateId,
        })),
        {
          onConflict: "client_id,scenario_template_id",
        },
      );

    if (insertAccessError) {
      throw insertAccessError;
    }
  }
}

async function backfillTrainingSessions(
  supabase: SupabaseClient,
  scenarioIndex: Map<
    string,
    {
      templateId: string;
      versionId: string;
      scenario: ScenarioDefinition;
    }
  >,
) {
  const { data: rows, error } = await supabase
    .from("training_sessions")
    .select(
      "id, client_id, scenario_id, scenario_slug, scenario_template_id, scenario_version_id, scenario_snapshot",
    );

  if (error) {
    throw error;
  }

  for (const row of (rows ?? []) as TrainingSessionBackfillRow[]) {
    const scenarioRef = row.scenario_slug ?? row.scenario_id;
    const match = scenarioIndex.get(scenarioRef);

    if (!match) {
      continue;
    }

    const updates: Record<string, unknown> = {};

    if (!row.scenario_slug) {
      updates.scenario_slug = match.scenario.id;
    }

    if (!row.scenario_template_id) {
      updates.scenario_template_id = match.templateId;
    }

    if (!row.scenario_version_id) {
      updates.scenario_version_id = match.versionId;
    }

    if (!row.scenario_snapshot) {
      updates.scenario_snapshot = toVersionPayload(match.scenario);
    }

    if (Object.keys(updates).length === 0) {
      continue;
    }

    const { error: updateError } = await supabase
      .from("training_sessions")
      .update(updates)
      .eq("id", row.id);

    if (updateError) {
      throw updateError;
    }
  }
}

async function main() {
  const supabase = createClient(
    getRequiredEnv("NEXT_PUBLIC_SUPABASE_URL"),
    getRequiredEnv("SUPABASE_SERVICE_ROLE_KEY"),
    {
      auth: {
        autoRefreshToken: false,
        persistSession: false,
      },
    },
  );

  const scenarioIndex = new Map<
    string,
    {
      templateId: string;
      versionId: string;
      scenario: ScenarioDefinition;
    }
  >();

  for (const scenario of scenarios) {
    const ids = await ensurePublishedScenario(supabase, scenario);
    scenarioIndex.set(scenario.id, {
      ...ids,
      scenario,
    });
    console.log(`Seeded scenario ${scenario.id}`);
  }

  await backfillLearnerAccess(
    supabase,
    [...scenarioIndex.values()].map((entry) => entry.templateId),
  );
  console.log("Backfilled default client access where no explicit assignments existed.");

  await backfillTrainingSessions(supabase, scenarioIndex);
  console.log("Backfilled training session scenario metadata.");
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
