import { describe, it, expect } from "vitest";
import { createTSQLCompletion } from "~/components/code/tsql/tsqlCompletion";
import type { TableSchema } from "@internal/tsql";

// Helper to create a mock completion context
function createMockContext(doc: string, pos: number, explicit = false) {
  return {
    state: {
      doc: {
        toString: () => doc,
        sliceString: (from: number, to: number) => doc.slice(from, to),
      },
    },
    pos,
    explicit,
    matchBefore: (regex: RegExp) => {
      const beforePos = doc.slice(0, pos);
      const match = beforePos.match(new RegExp(regex.source + "$"));
      if (match) {
        return {
          from: pos - match[0].length,
          to: pos,
          text: match[0],
        };
      }
      return null;
    },
  } as any;
}

// Test schema with enum columns
const testSchema: TableSchema[] = [
  {
    name: "runs",
    clickhouseName: "trigger_dev.task_runs_v2",
    tenantColumns: {
      organizationId: "organization_id",
      projectId: "project_id",
      environmentId: "environment_id",
    },
    description: "Task runs table",
    columns: {
      id: { name: "id", type: "String", description: "Run ID" },
      status: {
        name: "status",
        type: "LowCardinality(String)",
        description: "Run status",
        allowedValues: ["Completed", "Failed", "Queued", "Executing"],
      },
      machine: {
        name: "machine",
        type: "LowCardinality(String)",
        description: "Machine preset",
        allowedValues: ["micro", "small-1x", "small-2x", "medium-1x"],
      },
      environment_type: {
        name: "environment_type",
        type: "LowCardinality(String)",
        description: "Environment type",
        allowedValues: ["PRODUCTION", "STAGING", "DEVELOPMENT", "PREVIEW"],
      },
      created_at: { name: "created_at", type: "DateTime64", description: "Creation time" },
      organization_id: { name: "organization_id", type: "String" },
      project_id: { name: "project_id", type: "String" },
      environment_id: { name: "environment_id", type: "String" },
    },
  },
  {
    name: "logs",
    clickhouseName: "trigger_dev.task_events_v2",
    tenantColumns: {
      organizationId: "organization_id",
      projectId: "project_id",
      environmentId: "environment_id",
    },
    description: "Task logs table",
    columns: {
      id: { name: "id", type: "String" },
      run_id: { name: "run_id", type: "String" },
      message: { name: "message", type: "String" },
      level: { name: "level", type: "String" },
      timestamp: { name: "timestamp", type: "DateTime64" },
      organization_id: { name: "organization_id", type: "String" },
      project_id: { name: "project_id", type: "String" },
      environment_id: { name: "environment_id", type: "String" },
    },
  },
];

describe("createTSQLCompletion", () => {
  const completionSource = createTSQLCompletion(testSchema);

  it("should return null for empty input without explicit trigger", () => {
    const context = createMockContext("", 0, false);
    const result = completionSource(context);
    expect(result).toBeNull();
  });

  it("should return completions when explicitly triggered", () => {
    const context = createMockContext("", 0, true);
    const result = completionSource(context);
    expect(result).not.toBeNull();
    expect(result?.options.length).toBeGreaterThan(0);
  });

  it("should include tables in completions", () => {
    // When typing after FROM, tables should be available
    const doc = "SELECT * FROM r";
    const context = createMockContext(doc, doc.length, true);
    const result = completionSource(context);

    expect(result).not.toBeNull();

    const tableLabels = result?.options.map((o) => o.label);
    // Tables should always be available in completions
    expect(tableLabels).toContain("runs");
    expect(tableLabels).toContain("logs");
  });

  it("should suggest columns after SELECT keyword", () => {
    const doc = "SELECT FROM runs";
    // Position cursor right after SELECT
    const pos = 7;
    const context = createMockContext(doc, pos, true);
    const result = completionSource(context);

    expect(result).not.toBeNull();

    // Should include functions
    const labels = result?.options.map((o) => o.label) || [];
    expect(labels.some((l) => l === "count")).toBe(true);
    expect(labels.some((l) => l === "sum")).toBe(true);
  });

  it("should suggest columns with table prefix for qualified references", () => {
    const doc = "SELECT runs. FROM runs";
    // Position cursor right after "runs."
    const pos = 12;
    const context = createMockContext(doc, pos, true);
    const result = completionSource(context);

    expect(result).not.toBeNull();

    const columnLabels = result?.options.map((o) => o.label);
    expect(columnLabels).toContain("id");
    expect(columnLabels).toContain("status");
    expect(columnLabels).toContain("created_at");
  });

  it("should include SQL keywords in general context", () => {
    const doc = "S";
    const context = createMockContext(doc, doc.length, true);
    const result = completionSource(context);

    expect(result).not.toBeNull();

    const labels = result?.options.map((o) => o.label);
    expect(labels).toContain("SELECT");
  });

  it("should include aggregate functions", () => {
    const doc = "SELECT ";
    const context = createMockContext(doc, doc.length, true);
    const result = completionSource(context);

    expect(result).not.toBeNull();

    const labels = result?.options.map((o) => o.label);
    expect(labels).toContain("count");
    expect(labels).toContain("sum");
    expect(labels).toContain("avg");
    expect(labels).toContain("min");
    expect(labels).toContain("max");
  });

  it("should handle WHERE clause context", () => {
    const doc = "SELECT * FROM runs WHERE ";
    const context = createMockContext(doc, doc.length, true);
    const result = completionSource(context);

    expect(result).not.toBeNull();

    // Should suggest columns
    const labels = result?.options.map((o) => o.label) || [];
    expect(labels).toContain("status");

    // Should include conditional keywords
    expect(labels).toContain("AND");
    expect(labels).toContain("OR");
  });

  describe("enum value completions", () => {
    it("should suggest enum values after = operator", () => {
      const doc = "SELECT * FROM runs WHERE status = ";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).toContain("'Completed'");
      expect(labels).toContain("'Failed'");
      expect(labels).toContain("'Queued'");
      expect(labels).toContain("'Executing'");
    });

    it("should suggest enum values after = with opening quote", () => {
      const doc = "SELECT * FROM runs WHERE status = '";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).toContain("'Completed'");
      expect(labels).toContain("'Failed'");
    });

    it("should suggest enum values with partial input", () => {
      const doc = "SELECT * FROM runs WHERE status = 'Comp";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).toContain("'Completed'");
    });

    it("should suggest enum values for machine column", () => {
      const doc = "SELECT * FROM runs WHERE machine = ";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).toContain("'micro'");
      expect(labels).toContain("'small-1x'");
      expect(labels).toContain("'medium-1x'");
    });

    it("should suggest enum values for environment_type column", () => {
      const doc = "SELECT * FROM runs WHERE environment_type = ";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).toContain("'PRODUCTION'");
      expect(labels).toContain("'STAGING'");
      expect(labels).toContain("'DEVELOPMENT'");
    });

    it("should suggest enum values after != operator", () => {
      const doc = "SELECT * FROM runs WHERE status != ";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).toContain("'Completed'");
      expect(labels).toContain("'Failed'");
    });

    it("should suggest enum values after IN (", () => {
      const doc = "SELECT * FROM runs WHERE status IN (";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).toContain("'Completed'");
      expect(labels).toContain("'Failed'");
    });

    it("should suggest enum values after comma in IN clause", () => {
      const doc = "SELECT * FROM runs WHERE status IN ('Completed', ";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).toContain("'Failed'");
      expect(labels).toContain("'Queued'");
    });

    it("should include closing quote in replacement range when auto-paired", () => {
      // Simulate cursor between auto-paired quotes: status = '|'
      const doc = "SELECT * FROM runs WHERE status = ''";
      const pos = doc.length - 1; // Cursor is between the quotes
      const context = createMockContext(doc, pos, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();
      // The 'to' should extend past the cursor to include the closing quote
      expect(result?.to).toBe(pos + 1);
    });

    it("should not extend 'to' when no closing quote present", () => {
      const doc = "SELECT * FROM runs WHERE status = '";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();
      // 'to' should be undefined (or not set) since there's no closing quote
      expect(result?.to).toBeUndefined();
    });

    it("should not suggest enum values for columns without allowedValues", () => {
      const doc = "SELECT * FROM runs WHERE id = ";
      const context = createMockContext(doc, doc.length, true);
      const result = completionSource(context);

      expect(result).not.toBeNull();

      // Should return empty options for value context with non-enum column
      const labels = result?.options.map((o) => o.label) || [];
      expect(labels).not.toContain("'Completed'");
      expect(labels.length).toBe(0);
    });
  });
});
