"use client";

import { useState, useCallback } from "react";
import { Play, Send, Loader2 } from "lucide-react";
import { usePyodide } from "@/hooks/use-pyodide";
import { submitSolution } from "@/lib/api";
import { TestResults } from "./test-results";
import type {
  VisibleTestCase,
  HiddenTestInput,
  TestResult,
  HiddenTestOutput,
} from "@/types";

interface TestRunnerProps {
  code: string;
  functionName: string;
  slug: string;
  visibleTestCases: VisibleTestCase[];
  hiddenTestInputs: HiddenTestInput[];
}

interface ExecutionResult {
  test_id: number;
  output: unknown;
  error?: string;
}

export function TestRunner({
  code,
  functionName,
  slug,
  visibleTestCases,
  hiddenTestInputs,
}: TestRunnerProps) {
  const { pyodide, loading: pyodideLoading, runPython } = usePyodide();
  const [isRunning, setIsRunning] = useState(false);
  const [isSubmitting, setIsSubmitting] = useState(false);
  const [visibleResults, setVisibleResults] = useState<TestResult[]>([]);
  const [hiddenPassedCount, setHiddenPassedCount] = useState(0);
  const [hiddenTotalCount, setHiddenTotalCount] = useState(0);

  const generateTestHarness = useCallback(
    (tests: Array<{ id: number; input: Record<string, unknown> }>) => {
      const testsJson = JSON.stringify(tests);
      return `
import json

${code}

__tests__ = json.loads('${testsJson.replace(/'/g, "\\'")}')
__results__ = []

for test in __tests__:
    try:
        result = ${functionName}(**test["input"])
        __results__.append({"test_id": test["id"], "output": result})
    except Exception as e:
        __results__.append({"test_id": test["id"], "output": None, "error": str(e)})

json.dumps(__results__)
`;
    },
    [code, functionName]
  );

  const runTests = useCallback(
    async (tests: Array<{ id: number; input: Record<string, unknown> }>) => {
      const harness = generateTestHarness(tests);
      const { output, error } = await runPython(harness);

      if (error) {
        return tests.map((t) => ({
          test_id: t.id,
          output: null,
          error: error,
        }));
      }

      try {
        return JSON.parse(output as string) as ExecutionResult[];
      } catch {
        return tests.map((t) => ({
          test_id: t.id,
          output: null,
          error: "Failed to parse output",
        }));
      }
    },
    [generateTestHarness, runPython]
  );

  const handleRunTests = useCallback(async () => {
    if (!pyodide || isRunning) return;

    setIsRunning(true);
    setHiddenPassedCount(0);
    setHiddenTotalCount(0);

    try {
      const tests = visibleTestCases.map((tc) => ({
        id: tc.id,
        input: tc.input,
      }));
      const results = await runTests(tests);

      const testResults: TestResult[] = results.map((r, idx) => {
        const testCase = visibleTestCases[idx];
        const passed =
          !r.error &&
          JSON.stringify(r.output) === JSON.stringify(testCase.expected);

        return {
          test_id: r.test_id,
          passed,
          input: testCase.input,
          expected: testCase.expected,
          actual: r.output,
          error: r.error,
        };
      });

      setVisibleResults(testResults);
    } finally {
      setIsRunning(false);
    }
  }, [pyodide, isRunning, visibleTestCases, runTests]);

  const handleSubmit = useCallback(async () => {
    if (!pyodide || isSubmitting) return;

    setIsSubmitting(true);

    try {
      // Run visible tests first
      const visibleTests = visibleTestCases.map((tc) => ({
        id: tc.id,
        input: tc.input,
      }));
      const visibleExecutionResults = await runTests(visibleTests);

      const visibleTestResults: TestResult[] = visibleExecutionResults.map(
        (r, idx) => {
          const testCase = visibleTestCases[idx];
          const passed =
            !r.error &&
            JSON.stringify(r.output) === JSON.stringify(testCase.expected);

          return {
            test_id: r.test_id,
            passed,
            input: testCase.input,
            expected: testCase.expected,
            actual: r.output,
            error: r.error,
          };
        }
      );

      setVisibleResults(visibleTestResults);

      // Run hidden tests
      const hiddenTests = hiddenTestInputs.map((tc) => ({
        id: tc.id,
        input: tc.input,
      }));
      const hiddenExecutionResults = await runTests(hiddenTests);

      // Submit hidden outputs to server for validation
      const hiddenOutputs: HiddenTestOutput[] = hiddenExecutionResults.map(
        (r) => ({
          test_id: r.test_id,
          output: r.output,
        })
      );

      const response = await submitSolution(slug, {
        code,
        hidden_outputs: hiddenOutputs,
      });

      setHiddenPassedCount(response.total_passed);
      setHiddenTotalCount(response.total_tests);
    } catch (error) {
      console.error("Submission error:", error);
    } finally {
      setIsSubmitting(false);
    }
  }, [
    pyodide,
    isSubmitting,
    visibleTestCases,
    hiddenTestInputs,
    runTests,
    slug,
    code,
  ]);

  const isLoading = isRunning || isSubmitting;

  return (
    <div className="flex flex-col h-full">
      <div className="flex items-center gap-2 px-3 py-2 bg-[var(--secondary)] border-b border-[var(--border)]">
        <button
          onClick={handleRunTests}
          disabled={pyodideLoading || isLoading}
          className="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded bg-[var(--muted)] hover:bg-[var(--muted)]/80 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
        >
          {isRunning ? (
            <Loader2 className="h-4 w-4 animate-spin" />
          ) : (
            <Play className="h-4 w-4" />
          )}
          Run Tests
        </button>
        <button
          onClick={handleSubmit}
          disabled={pyodideLoading || isLoading}
          className="flex items-center gap-1.5 px-3 py-1.5 text-sm font-medium rounded bg-green-600 hover:bg-green-700 text-white disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
        >
          {isSubmitting ? (
            <Loader2 className="h-4 w-4 animate-spin" />
          ) : (
            <Send className="h-4 w-4" />
          )}
          Submit
        </button>
        {pyodideLoading && (
          <span className="text-sm text-[var(--muted-foreground)] ml-2">
            Loading Python runtime...
          </span>
        )}
      </div>
      <div className="flex-1 overflow-auto">
        <TestResults
          visibleResults={visibleResults}
          hiddenPassedCount={hiddenPassedCount}
          hiddenTotalCount={hiddenTotalCount}
          isRunning={isLoading}
          visibleTestCases={visibleTestCases}
        />
      </div>
    </div>
  );
}
