/**
 * ─────────────────────────────────────────────────────────────────────────────
 * OAUTH 2.0 — Google + GitHub providers
 * ─────────────────────────────────────────────────────────────────────────────
 *
 * Flow:
 *  1. User clicks "Continue with Google/GitHub"
 *  2. → GET /api/auth/oauth/[provider]        → builds authorization URL + PKCE state
 *  3. → Provider redirects back to /api/auth/oauth/callback/[provider]
 *  4. → Exchange code for tokens → fetch user profile
 *  5. → Find or create Clinic → create DB session → redirect to /dashboard
 *
 * Security:
 *  • PKCE state stored in httpOnly cookie (not localStorage)
 *  • State is cryptographically random 32-byte hex
 *  • Tokens stored encrypted at rest (in production use KMS/Vault)
 */

import { randomBytes } from "crypto";
import { cookies } from "next/headers";

// ── Provider config ───────────────────────────────────────────────────────────
export const OAUTH_PROVIDERS = {
  google: {
    name:         "Google",
    authUrl:      "https://accounts.google.com/o/oauth2/v2/auth",
    tokenUrl:     "https://oauth2.googleapis.com/token",
    userInfoUrl:  "https://www.googleapis.com/oauth2/v2/userinfo",
    clientId:     () => process.env.GOOGLE_CLIENT_ID ?? "",
    clientSecret: () => process.env.GOOGLE_CLIENT_SECRET ?? "",
    scope:        "openid email profile",
  },
  github: {
    name:         "GitHub",
    authUrl:      "https://github.com/login/oauth/authorize",
    tokenUrl:     "https://github.com/login/oauth/access_token",
    userInfoUrl:  "https://api.github.com/user",
    emailsUrl:    "https://api.github.com/user/emails",
    clientId:     () => process.env.GITHUB_CLIENT_ID ?? "",
    clientSecret: () => process.env.GITHUB_CLIENT_SECRET ?? "",
    scope:        "read:user user:email",
  },
} as const;

export type OAuthProvider = keyof typeof OAUTH_PROVIDERS;

const STATE_COOKIE = "clinera_oauth_state";
const STATE_TTL    = 10 * 60 * 1000; // 10 minutes

// ── Generate and store OAuth state ───────────────────────────────────────────
export async function generateOAuthState(provider: OAuthProvider): Promise<string> {
  const state = randomBytes(32).toString("hex");

  const cookieStore = await cookies();
  cookieStore.set({
    name:     `${STATE_COOKIE}_${provider}`,
    value:    state,
    httpOnly: true,
    secure:   process.env.NODE_ENV === "production",
    sameSite: "lax",
    maxAge:   STATE_TTL / 1000,
    path:     "/",
  });

  return state;
}

// ── Verify OAuth state ────────────────────────────────────────────────────────
export async function verifyOAuthState(
  provider: OAuthProvider,
  receivedState: string
): Promise<boolean> {
  const cookieStore = await cookies();
  const stored = cookieStore.get(`${STATE_COOKIE}_${provider}`)?.value;

  // Always clear state cookie regardless of result
  cookieStore.delete(`${STATE_COOKIE}_${provider}`);

  if (!stored) return false;
  return stored === receivedState;
}

// ── Build authorization URL ───────────────────────────────────────────────────
export function buildAuthorizationUrl(
  provider: OAuthProvider,
  state: string
): string {
  const config   = OAUTH_PROVIDERS[provider];
  const redirect = getRedirectUri(provider);
  const params   = new URLSearchParams({
    client_id:     config.clientId(),
    redirect_uri:  redirect,
    response_type: "code",
    scope:         config.scope,
    state,
  });

  // Google-specific extras
  if (provider === "google") {
    params.set("access_type", "offline");
    params.set("prompt", "select_account");
  }

  return `${config.authUrl}?${params.toString()}`;
}

// ── Exchange code for tokens ──────────────────────────────────────────────────
export async function exchangeCode(
  provider: OAuthProvider,
  code: string
): Promise<{ accessToken: string; refreshToken?: string; expiresAt?: Date }> {
  const config = OAUTH_PROVIDERS[provider];

  const params = new URLSearchParams({
    client_id:     config.clientId(),
    client_secret: config.clientSecret(),
    code,
    redirect_uri:  getRedirectUri(provider),
    grant_type:    "authorization_code",
  });

  const res = await fetch(config.tokenUrl, {
    method:  "POST",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Accept":       "application/json",
    },
    body: params.toString(),
  });

  if (!res.ok) {
    const text = await res.text();
    throw new Error(`Token exchange failed for ${provider}: ${text}`);
  }

  const data = await res.json();

  if (data.error) {
    throw new Error(`OAuth error: ${data.error} — ${data.error_description ?? ""}`);
  }

  return {
    accessToken:  data.access_token,
    refreshToken: data.refresh_token,
    expiresAt:    data.expires_in
      ? new Date(Date.now() + data.expires_in * 1000)
      : undefined,
  };
}

// ── Fetch user profile from provider ─────────────────────────────────────────
export interface OAuthProfile {
  providerId: string;
  email:      string;
  name:       string;
  avatarUrl?: string;
}

export async function fetchUserProfile(
  provider: OAuthProvider,
  accessToken: string
): Promise<OAuthProfile> {
  const config = OAUTH_PROVIDERS[provider];

  const res = await fetch(config.userInfoUrl, {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      Accept:        "application/json",
      "User-Agent":  "Clinera/1.0",
    },
  });

  if (!res.ok) {
    throw new Error(`Failed to fetch ${provider} profile: ${res.status}`);
  }

  const data = await res.json();

  if (provider === "google") {
    return {
      providerId: data.id,
      email:      data.email,
      name:       data.name ?? data.email.split("@")[0],
      avatarUrl:  data.picture,
    };
  }

  if (provider === "github") {
    // GitHub may have null email — fetch it separately
    let email = data.email as string | null;
    if (!email) {
      email = await fetchGitHubPrimaryEmail(accessToken);
    }
    if (!email) {
      throw new Error("GitHub account has no verified email address. Please add a public email to your GitHub profile.");
    }
    return {
      providerId: String(data.id),
      email,
      name:       data.name ?? data.login,
      avatarUrl:  data.avatar_url,
    };
  }

  throw new Error(`Unknown provider: ${provider}`);
}

async function fetchGitHubPrimaryEmail(accessToken: string): Promise<string | null> {
  const res = await fetch("https://api.github.com/user/emails", {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      Accept:        "application/vnd.github+json",
      "User-Agent":  "Clinera/1.0",
    },
  });

  if (!res.ok) return null;

  const emails: Array<{ email: string; primary: boolean; verified: boolean }> = await res.json();
  const primary = emails.find((e) => e.primary && e.verified);
  return primary?.email ?? emails.find((e) => e.verified)?.email ?? null;
}

// ── Helpers ───────────────────────────────────────────────────────────────────
function getRedirectUri(provider: OAuthProvider): string {
  const base = process.env.NEXT_PUBLIC_APP_URL ?? "http://localhost:3000";
  return `${base}/api/auth/oauth/callback/${provider}`;
}

export function isValidProvider(p: string): p is OAuthProvider {
  return p === "google" || p === "github";
}
