import { describe, expect, it } from "vitest";
import {
  getServiceEnvFiles,
  isDalSuiteType,
  normalizeTelemetryConfig,
  parseDotenvString,
  resolveLifecycleConfig,
  resolveSelectedDatabase,
  validateConfigCoverage,
  validateLifecycleConfig,
  validateRunnerManifest,
  validateServiceConfig,
  validateTelemetryConfig,
} from "./model.mjs";

describe("config-model", () => {
  it("parses dotenv text", () => {
    expect(
      parseDotenvString(`
# comment
FOO=bar
BAR="baz"
QUX='zap'
      `)
    ).toEqual({
      FOO: "bar",
      BAR: "baz",
      QUX: "zap",
    });
  });

  it("validates runner manifests", () => {
    expect(() =>
      validateRunnerManifest({
        services: {
          api: {
            suites: {
              integration: [
                { name: "health", files: ["tests/health.js"] },
              ],
            },
          },
        },
      })
    ).not.toThrow();

    expect(() =>
      validateRunnerManifest({
        services: {
          api: {
            suites: {
              integration: [
                { name: "health", files: ["a.js"], framework: "jest" },
              ],
            },
          },
        },
      })
    ).toThrow("unsupported framework");
  });

  it("validates config coverage", () => {
    const runner = {
      services: {
        api: { suites: {} },
      },
    };
    const config = {
      services: {
        frontend: {
          dependsOn: ["api"],
        },
      },
    };

    expect(() => validateConfigCoverage(runner, config)).toThrow(
      'Service "frontend" exists in testkit.config.json but not in runner.manifest.json'
    );
  });

  it("validates service and telemetry config", () => {
    expect(() =>
      validateServiceConfig("api", {
        local: {
          start: "npm run dev",
          baseUrl: "http://127.0.0.1:3000",
          readyUrl: "http://127.0.0.1:3000/health",
          port: 3000,
        },
      }, "testkit.config.json")
    ).not.toThrow();

    expect(() =>
      validateServiceConfig("api", {
        database: {
          backends: {},
        },
      }, "testkit.config.json")
    ).toThrow("database.backends is no longer supported");

    expect(() =>
      validateTelemetryConfig(
        {
          enabled: true,
          endpoint: "ftp://bad.example.com/upload",
          tokenEnv: "TOKEN",
        },
        "testkit.config.json"
      )
    ).toThrow("must use http or https");
  });

  it("collects env files and resolves lifecycle overrides", () => {
    expect(
      getServiceEnvFiles({
        envFile: ".env.shared",
        envFiles: [".env.local"],
      })
    ).toEqual([".env.shared", ".env.local"]);

    expect(() =>
      validateLifecycleConfig("api", { backends: {} }, "migrate")
    ).not.toThrow();

    expect(
      resolveLifecycleConfig(
        {
          cmd: "npm run migrate",
          backends: {
            local: {
              cmd: "npm run migrate:local",
            },
          },
        },
        "local"
      )
    ).toEqual({
      cmd: "npm run migrate:local",
    });
  });

  it("normalizes telemetry and database config", () => {
    expect(
      resolveSelectedDatabase("api", {
        database: {
          provider: "local",
          image: "pgvector/pgvector:pg16",
          template: {
            inputs: ["schema"],
          },
        },
      })
    ).toEqual({
      provider: "local",
      image: "pgvector/pgvector:pg16",
      selectedBackend: "local",
      reset: true,
      template: {
        inputs: ["schema"],
      },
    });

    expect(
      normalizeTelemetryConfig({
        enabled: true,
        endpoint: "https://example.com",
        tokenEnv: "TOKEN",
      })
    ).toEqual({
      enabled: true,
      endpoint: "https://example.com",
      tokenEnv: "TOKEN",
      timeoutMs: 3000,
    });
  });

  it("detects dal suite types", () => {
    const suite = { name: "db", files: ["a.js"] };
    const runnerService = {
      suites: {
        dal: [suite],
      },
    };
    expect(isDalSuiteType(suite, runnerService, runnerService.suites.dal)).toBe(true);
    expect(
      isDalSuiteType(
        { ...suite, framework: "playwright" },
        runnerService,
        runnerService.suites.dal
      )
    ).toBe(false);
  });
});
