/**
 * Integration Tests for Organization API Routes
 *
 * Tests the complete API layer for organization management.
 * These tests verify:
 * - GET /api/organization - Fetch organization data
 * - POST /api/organization/members - Create invitation
 * - GET /api/organization/members - List members
 * - DELETE /api/organization/members/[id] - Remove member
 * - GET /api/organization/seats - Get seat configuration
 */

import { jest } from "@jest/globals";
import { NextRequest, NextResponse } from "next/server";
import { createMockRequest } from "@/__tests__/helpers/request.helper";

// Mock auth
const mockGetAuthenticatedEmail = jest.fn();
jest.mock("@/lib/utils/auth-helpers.util", () => ({
  getAuthenticatedEmail: mockGetAuthenticatedEmail,
}));

// Mock auth module
jest.mock("@/lib/auth", () => ({
  auth: jest.fn(),
}));

// Mock CSRF
jest.mock("@/lib/csrf", () => ({
  validateCSRFToken: jest.fn().mockResolvedValue(true),
}));

// Mock rate limiter
jest.mock("@/lib/rate-limiter", () => ({
  applyRateLimit: jest.fn().mockResolvedValue({ success: true }),
  rateLimiters: {
    teamInvitation: { maxRequests: 10, windowMs: 3600000 },
  },
}));

// Mock Prisma
const mockPrisma = {
  users: {
    findUnique: jest.fn(),
  },
  organizations: {
    findUnique: jest.fn(),
  },
  organization_members: {
    findFirst: jest.fn(),
    findMany: jest.fn(),
    findUnique: jest.fn(),
    delete: jest.fn(),
  },
};

jest.mock("@/lib/db", () => ({
  prisma: mockPrisma,
}));

// Mock organization service
const mockOrganizationService = {
  getOrganizationByMember: jest.fn(),
  getMembers: jest.fn(),
  getSeatConfiguration: jest.fn(),
  getUserOrganizationRole: jest.fn(),
  getPendingInvitations: jest.fn(),
  inviteTeamMember: jest.fn(),
  removeMember: jest.fn(),
};

jest.mock("@/lib/services/organization.service", () => ({
  organizationService: mockOrganizationService,
}));

// Mock email service
jest.mock("@/lib/services/team-invitation-emails.service", () => ({
  sendTeamInvitation: jest.fn().mockResolvedValue({ success: true }),
}));

// Mock logger
jest.mock("@/lib/logger", () => ({
  logger: {
    info: jest.fn(),
    error: jest.fn(),
    warn: jest.fn(),
  },
}));

// Route migrated to Convex - mocks need updating
describe.skip("Organization API Routes", () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  describe("GET /api/organization", () => {
    it("should return organization data for authenticated owner", async () => {
      const { GET } = await import("@/app/api/organization/route");

      const userEmail = "owner@example.com";
      mockGetAuthenticatedEmail.mockResolvedValue(userEmail);

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_123",
        email: userEmail,
        name: "Owner User",
        companyName: "Test Clinic",
        subscriptions: [{ tierId: "tier3" }],
      });

      const now = new Date();
      const mockOrg = {
        id: "org_123",
        name: "Test Clinic",
        ownerId: "user_123",
        includedSeats: 1,
        purchasedSeats: 0,
        createdAt: now,
        updatedAt: now,
      };

      const mockMembers = [
        {
          id: "member_123",
          userId: "user_123",
          email: userEmail,
          name: "Owner User",
          role: "OWNER",
          joinedAt: now,
        },
      ];

      const mockSeatConfig = {
        tierId: "tier3",
        includedSeats: 1,
        purchasedSeats: 0,
        usedSeats: 0,
        availableSeats: 1,
      };

      mockOrganizationService.getOrganizationByMember.mockResolvedValue(
        mockOrg,
      );
      mockOrganizationService.getMembers.mockResolvedValue(mockMembers);
      mockOrganizationService.getSeatConfiguration.mockResolvedValue(
        mockSeatConfig,
      );
      mockOrganizationService.getUserOrganizationRole.mockResolvedValue({
        role: "OWNER",
        organizationId: "org_123",
        allowedTabs: ["billing", "team"],
      });
      mockOrganizationService.getPendingInvitations.mockResolvedValue([]);

      const request = new NextRequest("http://localhost:9000/api/organization");
      const response = await GET(request);
      const data = await response.json();

      expect(response.status).toBe(200);
      expect(data.success).toBe(true);
      expect(data.organization.id).toBe(mockOrg.id);
      expect(data.organization.name).toBe(mockOrg.name);
      expect(data.members).toHaveLength(1);
      expect(data.members[0].role).toBe("OWNER");
      expect(data.seatConfig).toEqual(mockSeatConfig);
      expect(data.userRole).toBe("OWNER");
    });

    it("should return 401 for unauthenticated requests", async () => {
      const { GET } = await import("@/app/api/organization/route");

      mockGetAuthenticatedEmail.mockResolvedValue(null);

      const request = new NextRequest("http://localhost:9000/api/organization");
      const response = await GET(request);
      const data = await response.json();

      expect(response.status).toBe(401);
      expect(data.success).toBe(false);
      expect(data.error).toBe("Unauthorized");
    });

    it("should return 404 if user has no organization", async () => {
      const { GET } = await import("@/app/api/organization/route");

      mockGetAuthenticatedEmail.mockResolvedValue("user@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_123",
        email: "user@example.com",
        name: "Test User",
        companyName: null,
        subscriptions: [{ tierId: "tier1" }], // tier1 users don't get auto-created orgs
      });

      mockOrganizationService.getOrganizationByMember.mockResolvedValue(null);

      const request = new NextRequest("http://localhost:9000/api/organization");
      const response = await GET(request);
      const data = await response.json();

      expect(response.status).toBe(404);
      expect(data.success).toBe(false);
      expect(data.hasOrganization).toBe(false);
    });

    it("should not include pending invitations for STAFF role", async () => {
      const { GET } = await import("@/app/api/organization/route");

      mockGetAuthenticatedEmail.mockResolvedValue("staff@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_123",
        email: "staff@example.com",
        name: "Staff User",
        companyName: null,
        subscriptions: [{ tierId: "tier3" }],
      });

      mockOrganizationService.getOrganizationByMember.mockResolvedValue({
        id: "org_123",
        name: "Test Clinic",
      });
      mockOrganizationService.getMembers.mockResolvedValue([]);
      mockOrganizationService.getSeatConfiguration.mockResolvedValue({
        tierId: "tier3",
        includedSeats: 1,
        purchasedSeats: 0,
        usedSeats: 1,
        availableSeats: 0,
      });
      mockOrganizationService.getUserOrganizationRole.mockResolvedValue({
        role: "STAFF",
        organizationId: "org_123",
        allowedTabs: ["appointments"],
      });

      const request = new NextRequest("http://localhost:9000/api/organization");
      const response = await GET(request);
      const data = await response.json();

      expect(response.status).toBe(200);
      expect(data.pendingInvitations).toEqual([]);
      expect(
        mockOrganizationService.getPendingInvitations,
      ).not.toHaveBeenCalled();
    });
  });

  describe("POST /api/organization/invitations", () => {
    it("should create invitation and send email (OWNER)", async () => {
      const { POST } = await import("@/app/api/organization/invitations/route");

      const userEmail = "owner@example.com";
      mockGetAuthenticatedEmail.mockResolvedValue(userEmail);

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_123",
        email: userEmail,
        name: "Owner User",
      });

      mockPrisma.organization_members.findFirst.mockResolvedValue({
        userId: "user_123",
        organizationId: "org_123",
        role: "OWNER",
        organization: {
          id: "org_123",
          name: "Test Clinic",
        },
      });

      const mockInvitation = {
        id: "invite_123",
        email: "newstaff@example.com",
        name: "New Staff",
        role: "STAFF",
        status: "PENDING",
        expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
        sentAt: new Date(),
        token: "secure_token_123",
      };

      mockOrganizationService.inviteTeamMember.mockResolvedValue(
        mockInvitation,
      );

      const requestBody = {
        email: "newstaff@example.com",
        name: "New Staff",
        role: "STAFF",
        csrfToken: "valid_csrf_token",
        lang: "en",
      };

      const request = new NextRequest(
        "http://localhost:9000/api/organization/invitations",
        {
          method: "POST",
          body: JSON.stringify(requestBody),
        },
      );

      const response = await POST(request);
      const data = await response.json();

      expect(response.status).toBe(200);
      expect(data.success).toBe(true);
      expect(data.invitation.email).toBe("newstaff@example.com");
    });

    it("should return 403 for STAFF role attempting to invite", async () => {
      const { POST } = await import("@/app/api/organization/invitations/route");

      mockGetAuthenticatedEmail.mockResolvedValue("staff@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_staff",
        email: "staff@example.com",
      });

      // STAFF role won't find a membership with OWNER/ADMIN
      mockPrisma.organization_members.findFirst.mockResolvedValue(null);

      const request = new NextRequest(
        "http://localhost:9000/api/organization/invitations",
        {
          method: "POST",
          body: JSON.stringify({
            email: "test@example.com",
            role: "STAFF",
          }),
        },
      );

      const response = await POST(request);
      const data = await response.json();

      expect(response.status).toBe(403);
      expect(data.error).toBe(
        "Only organization owners and admins can send invitations",
      );
    });

    it("should return 400 for invalid email", async () => {
      const { POST } = await import("@/app/api/organization/invitations/route");

      mockGetAuthenticatedEmail.mockResolvedValue("owner@example.com");

      const request = new NextRequest(
        "http://localhost:9000/api/organization/invitations",
        {
          method: "POST",
          body: JSON.stringify({
            email: "invalid-email",
            role: "STAFF",
          }),
        },
      );

      const response = await POST(request);
      const data = await response.json();

      expect(response.status).toBe(400);
      expect(data.error).toBe("Valid email is required");
    });

    it("should return 400 when no seats available", async () => {
      const { POST } = await import("@/app/api/organization/invitations/route");

      mockGetAuthenticatedEmail.mockResolvedValue("owner@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_123",
        email: "owner@example.com",
        name: "Owner",
      });

      mockPrisma.organization_members.findFirst.mockResolvedValue({
        userId: "user_123",
        organizationId: "org_123",
        role: "OWNER",
        organization: {
          id: "org_123",
          name: "Test Clinic",
        },
      });

      mockOrganizationService.inviteTeamMember.mockRejectedValue(
        new Error(
          "No available seats. Please upgrade your plan or purchase additional seats.",
        ),
      );

      const request = new NextRequest(
        "http://localhost:9000/api/organization/invitations",
        {
          method: "POST",
          body: JSON.stringify({
            email: "newstaff@example.com",
            role: "STAFF",
          }),
        },
      );

      const response = await POST(request);
      const data = await response.json();

      expect(response.status).toBe(500);
      expect(data.error).toContain("No available seats");
    });
  });

  describe.skip("GET /api/organization/members", () => {
    it("should return member list for OWNER/ADMIN", async () => {
      const { GET } = await import("@/app/api/organization/members/route");

      mockGetAuthenticatedEmail.mockResolvedValue("admin@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_admin",
        email: "admin@example.com",
      });

      mockOrganizationService.getUserOrganizationRole.mockResolvedValue({
        role: "ADMIN",
        organizationId: "org_123",
      });

      const mockMembers = [
        {
          id: "member_1",
          userId: "user_owner",
          email: "owner@example.com",
          name: "Owner",
          role: "OWNER",
          joinedAt: new Date(),
        },
        {
          id: "member_2",
          userId: "user_admin",
          email: "admin@example.com",
          name: "Admin",
          role: "ADMIN",
          joinedAt: new Date(),
        },
      ];

      mockOrganizationService.getMembers.mockResolvedValue(mockMembers);

      const request = new NextRequest(
        "http://localhost:9000/api/organization/members",
      );
      const response = await GET(request);
      const data = await response.json();

      expect(response.status).toBe(200);
      expect(data.success).toBe(true);
      expect(data.members).toHaveLength(2);
    });

    it("should return 403 for STAFF role", async () => {
      const { GET } = await import("@/app/api/organization/members/route");

      mockGetAuthenticatedEmail.mockResolvedValue("staff@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_staff",
        email: "staff@example.com",
      });

      mockOrganizationService.getUserOrganizationRole.mockResolvedValue({
        role: "STAFF",
        organizationId: "org_123",
      });

      const request = new NextRequest(
        "http://localhost:9000/api/organization/members",
      );
      const response = await GET(request);
      const data = await response.json();

      expect(response.status).toBe(403);
      expect(data.error).toBe("Insufficient permissions");
    });
  });

  describe.skip("DELETE /api/organization/members/[id]", () => {
    it("should remove member (OWNER)", async () => {
      const { DELETE } =
        await import("@/app/api/organization/members/[id]/route");

      mockGetAuthenticatedEmail.mockResolvedValue("owner@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_owner",
        email: "owner@example.com",
      });

      mockOrganizationService.getUserOrganizationRole.mockResolvedValue({
        role: "OWNER",
        organizationId: "org_123",
      });

      const mockMember = {
        id: "member_staff",
        userId: "user_staff",
        role: "STAFF",
        organizationId: "org_123",
      };

      mockPrisma.organization_members.findUnique.mockResolvedValue(mockMember);
      mockOrganizationService.removeMember.mockResolvedValue(undefined);

      const request = new NextRequest(
        "http://localhost:9000/api/organization/members/member_staff",
        {
          method: "DELETE",
        },
      );

      const params = { params: { id: "member_staff" } };
      const response = await DELETE(request, params);
      const data = await response.json();

      expect(response.status).toBe(200);
      expect(data.success).toBe(true);
      expect(mockOrganizationService.removeMember).toHaveBeenCalledWith(
        "member_staff",
        "user_owner",
      );
    });

    it("should return 403 for STAFF trying to remove member", async () => {
      const { DELETE } =
        await import("@/app/api/organization/members/[id]/route");

      mockGetAuthenticatedEmail.mockResolvedValue("staff@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_staff",
        email: "staff@example.com",
      });

      mockOrganizationService.getUserOrganizationRole.mockResolvedValue({
        role: "STAFF",
        organizationId: "org_123",
      });

      const request = new NextRequest(
        "http://localhost:9000/api/organization/members/member_123",
        {
          method: "DELETE",
        },
      );

      const params = { params: { id: "member_123" } };
      const response = await DELETE(request, params);
      const data = await response.json();

      expect(response.status).toBe(403);
      expect(data.error).toBe("Insufficient permissions");
    });
  });

  describe.skip("GET /api/organization/seats", () => {
    it("should return seat configuration for organization member", async () => {
      const { GET } = await import("@/app/api/organization/seats/route");

      mockGetAuthenticatedEmail.mockResolvedValue("user@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_123",
        email: "user@example.com",
      });

      mockOrganizationService.getUserOrganizationRole.mockResolvedValue({
        role: "ADMIN",
        organizationId: "org_123",
      });

      const mockSeatConfig = {
        tierId: "tier4",
        includedSeats: 2,
        purchasedSeats: 1,
        usedSeats: 2,
        availableSeats: 1,
      };

      mockOrganizationService.getSeatConfiguration.mockResolvedValue(
        mockSeatConfig,
      );

      const request = new NextRequest(
        "http://localhost:9000/api/organization/seats",
      );
      const response = await GET(request);
      const data = await response.json();

      expect(response.status).toBe(200);
      expect(data.success).toBe(true);
      expect(data.seatConfig).toEqual(mockSeatConfig);
    });

    it("should return 404 if user not in organization", async () => {
      const { GET } = await import("@/app/api/organization/seats/route");

      mockGetAuthenticatedEmail.mockResolvedValue("user@example.com");

      mockPrisma.users.findUnique.mockResolvedValue({
        id: "user_123",
        email: "user@example.com",
      });

      mockOrganizationService.getUserOrganizationRole.mockResolvedValue(null);

      const request = new NextRequest(
        "http://localhost:9000/api/organization/seats",
      );
      const response = await GET(request);
      const data = await response.json();

      expect(response.status).toBe(404);
      expect(data.error).toBe("Not a member of any organization");
    });
  });
});
