import { Webhook } from "svix";
import { headers } from "next/headers";
import { WebhookEvent } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
import Database from "better-sqlite3";
import path from "path";
import log from "@/lib/logger";

export async function POST(req: Request) {
  // Get the webhook secret from environment variables
  const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET;

  if (!WEBHOOK_SECRET) {
    throw new Error(
      "Please add CLERK_WEBHOOK_SECRET to your environment variables",
    );
  }

  // Get the headers
  const headerPayload = await headers();
  const svix_id = headerPayload.get("svix-id");
  const svix_timestamp = headerPayload.get("svix-timestamp");
  const svix_signature = headerPayload.get("svix-signature");

  // If there are no headers, error out
  if (!svix_id || !svix_timestamp || !svix_signature) {
    return NextResponse.json(
      { error: "Missing svix headers" },
      { status: 400 },
    );
  }

  // Get the body
  const payload = await req.json();
  const body = JSON.stringify(payload);

  // Create a new Svix instance with your webhook secret
  const wh = new Webhook(WEBHOOK_SECRET);

  let evt: WebhookEvent;

  // Verify the webhook signature
  try {
    evt = wh.verify(body, {
      "svix-id": svix_id,
      "svix-timestamp": svix_timestamp,
      "svix-signature": svix_signature,
    }) as WebhookEvent;
  } catch (err) {
    log.error("Webhook verification failed", err);
    return NextResponse.json({ error: "Verification failed" }, { status: 400 });
  }

  // Handle the webhook
  const eventType = evt.type;
  log.info("Received webhook event", {
    userId: evt.data.id,
    eventType,
  });

  try {
    // Connect to database directly for webhook operations
    const dbPath = path.join(process.cwd(), "data", "fitai.db");
    const db = new Database(dbPath);

    switch (eventType) {
      case "user.created": {
        const { id, email_addresses, first_name, last_name, public_metadata } =
          evt.data;

        // Get primary email
        const primaryEmail = email_addresses.find(
          (email) => email.id === evt.data.primary_email_address_id,
        );

        if (!primaryEmail?.email_address) {
          log.error(
            "No primary email found for user (user.created)",
            undefined,
            {
              userId: id,
            },
          );
          db.close();
          return NextResponse.json(
            { error: "No primary email" },
            { status: 400 },
          );
        }

        // Determine role & gym from metadata
        const role =
          (public_metadata?.role as
            | "superAdmin"
            | "admin"
            | "trainer"
            | "client"
            | "generalUser") || "client";
        let gymId = (public_metadata?.gymId as string | null) ?? null;
        const inviterUserId =
          (public_metadata?.inviterUserId as string | undefined) ?? undefined;
        const roleAssigned =
          (public_metadata?.roleAssigned as
            | "superAdmin"
            | "admin"
            | "trainer"
            | "client"
            | "generalUser"
            | undefined) ?? role;

        // Insert user into database with Clerk's user ID
        const now = new Date().toISOString();
        const stmt = db.prepare(`
          INSERT INTO users (id, email, first_name, last_name, password, phone, role, gym_id, created_at, updated_at)
          VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        `);

        stmt.run(
          id,
          primaryEmail.email_address,
          first_name || "",
          last_name || "",
          "", // Clerk handles authentication
          null, // phone
          role,
          gymId,
          now,
          now,
        );

        // Auto-create client record for self-registered users (role is "client")
        if (role === "client") {
          const clientId = `cli_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
          const clientStmt = db.prepare(`
            INSERT INTO clients (id, user_id, membership_type, membership_status, join_date, created_at, updated_at)
            VALUES (?, ?, ?, ?, ?, ?, ?)
          `);

          clientStmt.run(
            clientId,
            id,
            "basic", // Default membership type
            "active", // Default membership status
            now,
            now,
            now,
          );

          log.info("Client record auto-created for self-registered user", {
            userId: id,
            clientId,
          });
        }

        // If this is a client invited by a trainer, create trainer-client link
        if (roleAssigned === "client" && inviterUserId && gymId) {
          const inviterRow = db
            .prepare("SELECT role FROM users WHERE id = ?")
            .get(inviterUserId) as { role?: string } | undefined;

          if (inviterRow?.role === "trainer") {
            const linkId = `${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
            const linkStmt = db.prepare(`
              INSERT INTO trainer_clients (id, trainer_user_id, client_user_id, gym_id, created_at)
              VALUES (?, ?, ?, ?, ?)
            `);
            linkStmt.run(
              linkId,
              inviterUserId,
              id,
              gymId,
              new Date().toISOString(),
            );
          }
        }

        // If this is a trainer without a gymId but has an inviter, inherit inviter's gymId
        if (
          (roleAssigned === "trainer" || role === "trainer") &&
          !gymId &&
          inviterUserId
        ) {
          const inviterGymRow = db
            .prepare("SELECT gym_id FROM users WHERE id = ?")
            .get(inviterUserId) as { gym_id?: string } | undefined;

          if (inviterGymRow?.gym_id) {
            const inheritStmt = db.prepare(`
              UPDATE users
              SET gym_id = ?, updated_at = ?
              WHERE id = ?
            `);
            inheritStmt.run(inviterGymRow.gym_id, new Date().toISOString(), id);
            gymId = inviterGymRow.gym_id;
          }
        }

        log.info("User created in database", {
          userId: id,
          role,
          gymId: gymId ?? null,
        });
        db.close();
        break;
      }

      case "user.updated": {
        const { id, email_addresses, first_name, last_name, public_metadata } =
          evt.data;

        // Get primary email
        const primaryEmail = email_addresses.find(
          (email) => email.id === evt.data.primary_email_address_id,
        );

        if (!primaryEmail?.email_address) {
          log.error(
            "No primary email found for user (user.updated)",
            undefined,
            {
              userId: id,
            },
          );
          db.close();
          return NextResponse.json(
            { error: "No primary email" },
            { status: 400 },
          );
        }

        // Determine role & gym from metadata
        const role =
          (public_metadata?.role as
            | "superAdmin"
            | "admin"
            | "trainer"
            | "client"
            | "generalUser") || "client";
        let gymId = (public_metadata?.gymId as string | null) ?? null;

        // Update user in database
        const now = new Date().toISOString();
        const stmt = db.prepare(`
          UPDATE users
          SET email = ?, first_name = ?, last_name = ?, role = ?, gym_id = ?, updated_at = ?
          WHERE id = ?
        `);

        stmt.run(
          primaryEmail.email_address,
          first_name || "",
          last_name || "",
          role,
          gymId,
          now,
          id,
        );

        // If user is a trainer and gymId is missing, attempt to inherit from inviter when available
        if (
          role === "trainer" &&
          !gymId &&
          evt.data.public_metadata?.inviterUserId
        ) {
          const inviterUserId = String(evt.data.public_metadata.inviterUserId);
          const inviterGymRow = db
            .prepare("SELECT gym_id FROM users WHERE id = ?")
            .get(inviterUserId) as { gym_id?: string } | undefined;

          if (inviterGymRow?.gym_id) {
            const inheritStmt = db.prepare(`
              UPDATE users
              SET gym_id = ?, updated_at = ?
              WHERE id = ?
            `);
            inheritStmt.run(inviterGymRow.gym_id, new Date().toISOString(), id);
            gymId = inviterGymRow.gym_id;
          }
        }

        log.info("User updated in database", { userId: id });
        db.close();
        break;
      }

      case "user.deleted": {
        const { id } = evt.data;

        if (!id) {
          log.error("No user ID provided for deletion");
          db.close();
          return NextResponse.json({ error: "No user ID" }, { status: 400 });
        }

        // Delete user from database (cascade will handle related records)
        const stmt = db.prepare("DELETE FROM users WHERE id = ?");
        stmt.run(id);

        log.info("User deleted from database", { userId: id });
        db.close();
        break;
      }

      default:
        log.debug("Unhandled webhook event type", { eventType });
        db.close();
    }

    return NextResponse.json(
      { message: "Webhook processed successfully" },
      { status: 200 },
    );
  } catch (error) {
    log.error("Webhook processing failed", error);
    return NextResponse.json({ error: "Processing failed" }, { status: 500 });
  }
}
