/**
 * GET /api/auth/oauth/callback/[provider]
 *
 * OAuth callback handler. Validates state, exchanges code for tokens,
 * fetches user profile, finds or creates a Clinic, creates a DB session.
 *
 * Edge cases handled:
 *  • Provider returns error (user denied, etc.)
 *  • State mismatch (CSRF)
 *  • Email from OAuth already registered with email/password → link accounts
 *  • Same provider re-login → refresh tokens
 *  • New OAuth user → auto-register with 14-day trial
 */

import { NextResponse } from "next/server";
import { prisma } from "@/lib/db";
import {
  isValidProvider,
  verifyOAuthState,
  exchangeCode,
  fetchUserProfile,
  type OAuthProvider,
} from "@/lib/oauth";
import {
  createSession,
  setSessionCookie,
  writeAuditLog,
  extractRequestMeta,
} from "@/lib/auth";
import { seedDefaultTemplates } from "@/lib/reminders";
import { oauthLimiter, getLimiterKey, tooManyRequests } from "@/lib/ratelimit";

const APP_URL = () => process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";

function errorRedirect(msg: string): NextResponse {
  const url = new URL("/login", APP_URL());
  url.searchParams.set("error", msg);
  return NextResponse.redirect(url.toString());
}

export async function GET(
  req: Request,
  { params }: { params: Promise<{ provider: string }> }
) {
  // ── Rate limiting ──────────────────────────────────────────────────────────
  const limitKey = getLimiterKey(req, "oauth-cb");
  const limit    = oauthLimiter.check(limitKey);
  if (!limit.allowed) return tooManyRequests(limit.resetIn);

  const { provider } = await params;

  if (!isValidProvider(provider)) {
    return errorRedirect("Invalid OAuth provider");
  }

  const url        = new URL(req.url);
  const code       = url.searchParams.get("code");
  const state      = url.searchParams.get("state");
  const oauthError = url.searchParams.get("error");

  // ── Provider returned an error (user denied access) ───────────────────────
  if (oauthError) {
    const desc = url.searchParams.get("error_description") ?? oauthError;
    return errorRedirect(`OAuth cancelled: ${desc}`);
  }

  if (!code || !state) {
    return errorRedirect("Missing code or state in OAuth callback");
  }

  // ── Verify CSRF state ─────────────────────────────────────────────────────
  const stateValid = await verifyOAuthState(provider as OAuthProvider, state);
  if (!stateValid) {
    return errorRedirect("OAuth state mismatch. Please try again.");
  }

  try {
    // ── Exchange code for tokens ───────────────────────────────────────────
    const tokens = await exchangeCode(provider as OAuthProvider, code);

    // ── Fetch user profile ─────────────────────────────────────────────────
    const profile = await fetchUserProfile(provider as OAuthProvider, tokens.accessToken);

    const meta = extractRequestMeta(req);

    // ── Find existing OAuth account ────────────────────────────────────────
    const existingOAuth = await prisma.oAuthAccount.findUnique({
      where: {
        provider_providerId: {
          provider,
          providerId: profile.providerId,
        },
      },
      include: { clinic: { select: { id: true, email: true } } },
    });

    let clinicId: string;

    if (existingOAuth) {
      // ── Returning OAuth user — update tokens ─────────────────────────────
      clinicId = existingOAuth.clinicId;

      await prisma.oAuthAccount.update({
        where: { id: existingOAuth.id },
        data: {
          accessToken:  tokens.accessToken,
          refreshToken: tokens.refreshToken ?? undefined,
          expiresAt:    tokens.expiresAt ?? undefined,
          name:         profile.name,
          avatarUrl:    profile.avatarUrl ?? undefined,
          email:        profile.email,
          updatedAt:    new Date(),
        },
      });

      await writeAuditLog({
        clinicId,
        action:   `login.${provider}`,
        actor:    profile.email,
        request:  req,
      });
    } else {
      // ── New OAuth login ───────────────────────────────────────────────────
      // Check if a clinic with this email already exists (email/password signup)
      const existingClinic = await prisma.clinic.findUnique({
        where: { email: profile.email.toLowerCase() },
        select: { id: true, email: true },
      });

      if (existingClinic) {
        // ── Link OAuth to existing clinic ─────────────────────────────────
        clinicId = existingClinic.id;

        await prisma.oAuthAccount.create({
          data: {
            clinicId,
            provider,
            providerId:   profile.providerId,
            email:        profile.email,
            name:         profile.name,
            avatarUrl:    profile.avatarUrl ?? undefined,
            accessToken:  tokens.accessToken,
            refreshToken: tokens.refreshToken ?? undefined,
            expiresAt:    tokens.expiresAt ?? undefined,
          },
        });

        await writeAuditLog({
          clinicId,
          action:   "oauth.connect",
          actor:    profile.email,
          metadata: { provider, linkedToExisting: true },
          request:  req,
        });
      } else {
        // ── No account found — block login, must sign up first ────────────
        return errorRedirect("No account found for this email. Please sign up first.");
      }
    }

    // ── Create DB session ──────────────────────────────────────────────────
    const token = await createSession(clinicId, meta);
    await setSessionCookie(token);

    // ── Redirect to dashboard ──────────────────────────────────────────────
    return NextResponse.redirect(new URL("/dashboard", APP_URL()).toString());
  } catch (err) {
    console.error(`[OAuth ${provider} callback]`, err);
    const message = err instanceof Error ? err.message : "OAuth authentication failed";
    return errorRedirect(encodeURIComponent(message));
  }
}
