import { describe, it, expect, beforeEach, vi } from "vitest";
import { appRouter } from "./routers";
import type { TrpcContext } from "./_core/context";

// Mock database functions
vi.mock("./db", () => ({
  getKhayoPersonality: vi.fn(),
  updateKhayoPersonality: vi.fn(),
  createConversation: vi.fn(),
  getUserConversations: vi.fn(),
  getConversation: vi.fn(),
  getConversationMessages: vi.fn(),
  addMessage: vi.fn(),
  recordSelfUpdate: vi.fn(),
}));

// Mock LLM
vi.mock("./_core/llm", () => ({
  invokeLLM: vi.fn(),
}));

// Mock notifications
vi.mock("./_core/notification", () => ({
  notifyOwner: vi.fn(),
}));

// Mock ENV
vi.mock("./_core/env", () => ({
  ENV: {
    ownerOpenId: "owner-123",
  },
}));

function createAuthContext(openId: string): TrpcContext {
  return {
    user: {
      id: 1,
      openId,
      email: "test@example.com",
      name: "Test User",
      loginMethod: "test",
      role: "user",
      createdAt: new Date(),
      updatedAt: new Date(),
      lastSignedIn: new Date(),
    },
    req: {
      protocol: "https",
      headers: {},
    } as TrpcContext["req"],
    res: {
      clearCookie: vi.fn(),
    } as unknown as TrpcContext["res"],
  };
}

describe("Khayo Router - Authentication & Authorization", () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe("isOwner procedure", () => {
    it("should allow owner access", async () => {
      const ctx = createAuthContext("owner-123");
      const caller = appRouter.createCaller(ctx);

      const result = await caller.khayo.isOwner();
      expect(result.isOwner).toBe(true);
    });

    it("should deny non-owner access", async () => {
      const ctx = createAuthContext("user-456");
      const caller = appRouter.createCaller(ctx);

      await expect(caller.khayo.isOwner()).rejects.toThrow("Unauthorized access to Khayo");
    });
  });

  describe("getPersonality procedure", () => {
    it("should return personality for owner", async () => {
      const ctx = createAuthContext("owner-123");
      const caller = appRouter.createCaller(ctx);

      const result = await caller.khayo.getPersonality();
      expect(result).toBeDefined();
      expect(result.directive).toBeDefined();
    });

    it("should deny non-owner access", async () => {
      const ctx = createAuthContext("user-456");
      const caller = appRouter.createCaller(ctx);

      await expect(caller.khayo.getPersonality()).rejects.toThrow("Unauthorized access");
    });
  });

  describe("createConversation procedure", () => {
    it("should deny non-owner access", async () => {
      const ctx = createAuthContext("user-456");
      const caller = appRouter.createCaller(ctx);

      await expect(
        caller.khayo.createConversation({ title: "Test Chat" })
      ).rejects.toThrow("Unauthorized access");
    });
  });

  describe("getConversations procedure", () => {
    it("should return conversations for owner", async () => {
      const ctx = createAuthContext("owner-123");
      const caller = appRouter.createCaller(ctx);

      const result = await caller.khayo.getConversations();
      expect(Array.isArray(result)).toBe(true);
    });

    it("should deny non-owner access", async () => {
      const ctx = createAuthContext("user-456");
      const caller = appRouter.createCaller(ctx);

      await expect(caller.khayo.getConversations()).rejects.toThrow("Unauthorized access");
    });
  });

  describe("getMessages procedure", () => {
    it("should deny non-owner access", async () => {
      const ctx = createAuthContext("user-456");
      const caller = appRouter.createCaller(ctx);

      await expect(
        caller.khayo.getMessages({ conversationId: 1 })
      ).rejects.toThrow("Unauthorized access");
    });
  });

  describe("sendMessage procedure", () => {
    it("should deny non-owner access", async () => {
      const ctx = createAuthContext("user-456");
      const caller = appRouter.createCaller(ctx);

      await expect(
        caller.khayo.sendMessage({
          conversationId: 1,
          message: "Hello",
        })
      ).rejects.toThrow("Unauthorized access");
    });
  });

  describe("applyUpdate procedure", () => {
    it("should deny non-owner access", async () => {
      const ctx = createAuthContext("user-456");
      const caller = appRouter.createCaller(ctx);

      await expect(
        caller.khayo.applyUpdate({
          updateDirective: "Be more helpful",
        })
      ).rejects.toThrow("Unauthorized access");
    });
  });

  describe("Owner-only access control", () => {
    it("should verify owner identity on every protected route", async () => {
      const ownerCtx = createAuthContext("owner-123");
      const unauthorizedCtx = createAuthContext("other-user");

      const ownerCaller = appRouter.createCaller(ownerCtx);
      const unauthorizedCaller = appRouter.createCaller(unauthorizedCtx);

      // Owner should succeed
      const ownerResult = await ownerCaller.khayo.isOwner();
      expect(ownerResult.isOwner).toBe(true);

      // Unauthorized should fail
      await expect(unauthorizedCaller.khayo.isOwner()).rejects.toThrow();
    });

    it("should consistently block unauthorized users across all procedures", async () => {
      const unauthorizedCtx = createAuthContext("unauthorized-user");
      const caller = appRouter.createCaller(unauthorizedCtx);

      // Test all protected procedures
      const procedures = [
        () => caller.khayo.isOwner(),
        () => caller.khayo.getPersonality(),
        () => caller.khayo.getConversations(),
        () => caller.khayo.createConversation({ title: "Test" }),
        () => caller.khayo.getMessages({ conversationId: 1 }),
        () => caller.khayo.sendMessage({ conversationId: 1, message: "test" }),
        () => caller.khayo.applyUpdate({ updateDirective: "test" }),
      ];

      for (const procedure of procedures) {
        await expect(procedure()).rejects.toThrow();
      }
    });
  });


});
