import { Hono } from "hono";
import { testClient } from "hono/testing";
import { describe, expect, it } from "vitest";
import { createConfig } from "../../config.js";
import { MiddlewareManager } from "../../middlewares/middleware.js";

describe("Middleware Tests", () => {
  // Create a config with test Supabase credentials but development NODE_ENV
  // This allows us to test actual auth validation against the test database
  const config = { ...createConfig(), NODE_ENV: "development" as const };
  MiddlewareManager.initialize(config);
  const middlewareManager = MiddlewareManager.getInstance();

  describe("Supabase Middleware", () => {
    it("should inject supabase client into context", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.get("/test", (c) => {
        const supabase = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("supabase");
        return c.json({ hasSupabase: !!supabase });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasSupabase).toBe(true);
    });
  });

  describe("Auth Middleware", () => {
    it("should reject requests without authorization header", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.auth);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(401);
      expect(data.error).toBe("Missing or invalid authorization header");
    });

    it("should reject requests with invalid Bearer prefix", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.auth);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: "Token invalid-format",
        },
      });
      const data = await res.json();

      expect(res.status).toBe(401);
      expect(data.error).toBe("Missing or invalid authorization header");
    });

    it("should reject requests with invalid token", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.auth);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: "Bearer invalid-token-xyz-123",
        },
      });
      const data = await res.json();

      expect(res.status).toBe(401);
      // Should get auth error (may vary based on token format)
      expect(data.error.includes("Invalid") || data.error.includes("authorization")).toBeTruthy();
    });

    it("should reject requests with empty Bearer token", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.auth);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: "Bearer ",
        },
      });
      const data = await res.json();

      expect(res.status).toBe(401);
      // May get "Missing or invalid authorization header" or "Invalid or expired token"
      expect(data.error.includes("Invalid") || data.error.includes("authorization")).toBeTruthy();
    });
  });

  describe("Maybe Authenticated Middleware", () => {
    it("should allow requests without authorization header", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.maybeAuthenticated);
      app.get("/test", (c) => {
        const user = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("user");
        return c.json({ hasUser: !!user, user });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasUser).toBe(false);
      expect(data.user).toBe(null);
    });

    it("should set user to null with invalid token", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.maybeAuthenticated);
      app.get("/test", (c) => {
        const user = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("user");
        return c.json({ hasUser: !!user, user });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: "Bearer invalid-token",
        },
      });
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasUser).toBe(false);
      expect(data.user).toBe(null);
    });

    it("should ignore malformed authorization header", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.maybeAuthenticated);
      app.get("/test", (c) => {
        const user = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("user");
        return c.json({ hasUser: !!user });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: "Invalid format",
        },
      });
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasUser).toBe(false);
    });
  });

  describe("Basic Auth Middleware", () => {
    it("should reject requests without authorization header", async () => {
      const app = new Hono();
      app.use(middlewareManager.basicAuth);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(401);
      expect(data.error).toBe("Missing or invalid authorization header");
    });

    it("should reject requests with Bearer instead of Basic", async () => {
      const app = new Hono();
      app.use(middlewareManager.basicAuth);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: "Bearer some-token",
        },
      });
      const data = await res.json();

      expect(res.status).toBe(401);
      expect(data.error).toBe("Missing or invalid authorization header");
    });

    it("should reject requests with invalid secret", async () => {
      const app = new Hono();
      app.use(middlewareManager.basicAuth);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: "Basic wrong-secret",
        },
      });
      const data = await res.json();

      expect(res.status).toBe(401);
      expect(
        data.error === "Unauthorized" || data.error === "Missing or invalid authorization header"
      ).toBeTruthy();
    });

    it("should accept requests with correct secret", async () => {
      const app = new Hono();
      app.use(middlewareManager.basicAuth);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: `Basic ${config.TASKS_SECRET}`,
        },
      });

      // Basic auth should work if TASKS_SECRET is set, otherwise it will fail
      expect(res.status === 200 || res.status === 401).toBeTruthy();
    });
  });

  describe("Regular User Check Middleware", () => {
    it("should check if user profile exists", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.auth);
      app.use(middlewareManager.regularUserCheck);
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get({
        headers: {
          Authorization: "Bearer invalid-token",
        },
      });

      // Should fail due to invalid token in auth middleware
      expect(res.status).toBe(401);
    });
  });

  describe("StreamChat Middleware", () => {
    it("should inject StreamChat client into context", async () => {
      const app = new Hono();
      app.use(middlewareManager.streamChat);
      app.get("/test", (c) => {
        const streamClient = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("streamServerClient");
        return c.json({ hasStreamClient: !!streamClient });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasStreamClient).toBe(true);
    });
  });

  describe("R2 Middleware", () => {
    it("should inject S3 client into context", async () => {
      const app = new Hono();
      app.use(middlewareManager.r2);
      app.get("/test", (c) => {
        const s3Client = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("s3_client");
        return c.json({ hasS3Client: !!s3Client });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasS3Client).toBe(true);
    });
  });

  describe("Transporter Middleware", () => {
    it("should inject email transporter into context", async () => {
      const app = new Hono();
      app.use(middlewareManager.transporter);
      app.get("/test", (c) => {
        const transporter = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("transporter");
        return c.json({ hasTransporter: !!transporter });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasTransporter).toBe(true);
    });
  });

  describe("Stripe Middleware", () => {
    it("should inject Stripe client into context", async () => {
      const app = new Hono();
      app.use(middlewareManager.stripe);
      app.get("/test", (c) => {
        const stripe = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("stripe");
        return c.json({ hasStripe: !!stripe });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasStripe).toBe(true);
    });
  });

  describe("Stripe Sync Middleware", () => {
    it("should inject Stripe Sync client into context", async () => {
      const app = new Hono();
      app.use(middlewareManager.stripeSync);
      app.get("/test", (c) => {
        const stripeSync = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("stripeSync");
        return c.json({ hasStripeSync: !!stripeSync });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasStripeSync).toBe(true);
    });
  });

  describe("Middleware Chaining", () => {
    it("should chain multiple middlewares correctly", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.streamChat);
      app.use(middlewareManager.stripe);
      app.get("/test", (c) => {
        const supabase = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("supabase");
        const streamClient = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("streamServerClient");
        const stripe = // biome-ignore lint/suspicious/noExplicitAny: Needed for context access in tests
          (c as any).get("stripe");
        return c.json({
          hasSupabase: !!supabase,
          hasStreamClient: !!streamClient,
          hasStripe: !!stripe,
        });
      });

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(200);
      expect(data.hasSupabase).toBe(true);
      expect(data.hasStreamClient).toBe(true);
      expect(data.hasStripe).toBe(true);
    });

    it("should stop middleware chain on auth failure", async () => {
      const app = new Hono();
      app.use(middlewareManager.supabase);
      app.use(middlewareManager.auth); // This will fail
      app.use(middlewareManager.streamChat); // This should not execute
      app.get("/test", (c) => c.json({ success: true }));

      // biome-ignore lint/suspicious/noExplicitAny: testClient requires any for dynamic route access
      const client = testClient(app) as any;
      const res = await client.test.$get();
      const data = await res.json();

      expect(res.status).toBe(401);
      expect(data.error).toBe("Missing or invalid authorization header");
    });
  });

  describe("MiddlewareManager Singleton", () => {
    it("should maintain singleton instance", () => {
      const instance1 = MiddlewareManager.getInstance();
      const instance2 = MiddlewareManager.getInstance();

      expect(instance1).toBe(instance2);
    });
  });

  // Note: Tests for real test user authentication are covered by route tests
  // which use the test database infrastructure. The middleware tests above
  // validate auth logic with invalid tokens, which is sufficient for unit testing.
});
