/**
 * OIDC Provider configuration for Ghost Guild.
 *
 * ghostguild.org acts as the identity provider. Outline wiki is the sole
 * relying party (client). Members authenticate via the existing magic-link
 * flow, and the provider issues standard OIDC tokens so Outline can identify
 * them.
 */
import Provider from "oidc-provider";
import { MongoAdapter } from "./oidc-mongodb-adapter.js";
import Member from "../models/member.js";
import { connectDB } from "./mongoose.js";

if (process.env.NODE_ENV === 'production' && !process.env.OIDC_COOKIE_SECRET) {
  throw new Error('OIDC_COOKIE_SECRET must be set in production')
}

/**
 * Renders a standalone HTML page in the guild dark style.
 * Used for OIDC logout/error screens that are served outside Nuxt.
 */
function guildPageShell(title: string, bodyContent: string, extraStyles = "") {
  return `<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>${title} — Ghost Guild</title>
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    body {
      font-family: 'Inter', system-ui, -apple-system, sans-serif;
      background-color: #1a1510;
      background-image:
        radial-gradient(ellipse at 20% 50%, rgba(154, 111, 44, 0.06) 0%, transparent 60%),
        radial-gradient(ellipse at 80% 50%, rgba(154, 111, 44, 0.04) 0%, transparent 60%);
      color: #bfb3a2;
      min-height: 100vh;
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 1rem;
    }
    .card {
      background-color: #2a241c;
      border: 1px solid rgba(154, 111, 44, 0.15);
      border-radius: 12px;
      box-shadow: 0 0 30px rgba(208, 158, 78, 0.06);
      padding: 2.5rem;
      width: 100%;
      max-width: 420px;
      text-align: center;
    }
    h1 {
      font-family: 'Inter', system-ui, -apple-system, sans-serif;
      font-size: 1.5rem;
      font-weight: 700;
      color: #d09e4e;
      margin-bottom: 0.75rem;
    }
    p { line-height: 1.6; margin-bottom: 1rem; }
    .subtext { font-size: 0.875rem; color: #6b5f4d; }
    .btn-primary {
      display: inline-block;
      background-color: #d09e4e;
      color: #1a1510;
      padding: 0.625rem 1.5rem;
      border-radius: 6px;
      border: none;
      font-size: 0.875rem;
      font-weight: 700;
      cursor: pointer;
      text-decoration: none;
      transition: background-color 0.15s;
    }
    .btn-primary:hover { background-color: #e0b86e; }
    .btn-secondary {
      display: inline-block;
      background-color: transparent;
      color: #f0ebe4;
      padding: 0.625rem 1.5rem;
      border-radius: 6px;
      border: 1px solid rgba(208, 158, 78, 0.4);
      font-size: 0.875rem;
      font-weight: 400;
      cursor: pointer;
      text-decoration: none;
      transition: border-color 0.15s, color 0.15s;
    }
    .btn-secondary:hover {
      border-color: rgba(224, 184, 110, 0.6);
      color: #f5e6c5;
    }
    .actions { display: flex; gap: 0.75rem; justify-content: center; margin-top: 1.5rem; }
    .brand {
      margin-top: 2rem;
      font-family: 'Inter', system-ui, -apple-system, sans-serif;
      font-size: 0.75rem;
      font-variant: small-caps;
      letter-spacing: 0.05em;
      color: #6b5f4d;
    }
    .error-detail {
      margin-top: 1rem;
      background-color: #1a1510;
      border: 1px solid rgba(154, 111, 44, 0.1);
      border-radius: 6px;
      padding: 1rem;
      font-family: 'Ubuntu Mono', 'Courier New', monospace;
      font-size: 0.75rem;
      color: #6b5f4d;
      text-align: left;
      word-break: break-word;
    }
    ${extraStyles}
  </style>
</head>
<body>
  <div class="card">
    ${bodyContent}
  </div>
</body>
</html>`;
}

let _provider: InstanceType<typeof Provider> | null = null;

export async function getOidcProvider() {
  if (_provider) return _provider;

  const config = useRuntimeConfig();
  const issuer = process.env.OIDC_ISSUER || "https://ghostguild.org";

  _provider = new Provider(issuer, {
    adapter: MongoAdapter,

    // Trust X-Forwarded-Proto from Traefik reverse proxy
    proxy: true,

    clients: [
      {
        client_id: process.env.OIDC_CLIENT_ID || "outline-wiki",
        client_secret: process.env.OIDC_CLIENT_SECRET || "",
        redirect_uris: [
          "https://wiki.ghostguild.org/auth/oidc.callback",
          // Local development callback
          "http://localhost:3100/auth/oidc.callback",
        ],
        post_logout_redirect_uris: [
          "https://wiki.ghostguild.org",
          "http://localhost:3100",
        ],
        grant_types: ["authorization_code", "refresh_token"],
        response_types: ["code"],
        token_endpoint_auth_method: "client_secret_post",
      },
    ],

    claims: {
      openid: ["sub"],
      profile: ["name", "preferred_username"],
      email: ["email", "email_verified"],
    },

    scopes: ["openid", "profile", "email", "offline_access"],

    findAccount: async (_ctx: unknown, id: string) => {
      await connectDB();
      const member = await (Member as any).findById(id);
      if (!member) return undefined;

      return {
        accountId: id,
        async claims(_use: string, _scope: string) {
          return {
            sub: id,
            name: member.name,
            preferred_username: member.name,
            email: member.email,
            email_verified: true,
          };
        },
      };
    },

    cookies: {
      keys: (process.env.OIDC_COOKIE_SECRET || "dev-cookie-secret").split(","),
    },

    ttl: {
      AccessToken: 3600, // 1 hour
      AuthorizationCode: 600, // 10 minutes
      RefreshToken: 14 * 24 * 60 * 60, // 14 days
      Session: 14 * 24 * 60 * 60, // 14 days
      Interaction: 600, // 10 minutes
      Grant: 14 * 24 * 60 * 60, // 14 days
    },

    features: {
      devInteractions: {
        enabled: process.env.NODE_ENV !== "production",
      },
      revocation: { enabled: true },
      rpInitiatedLogout: {
        enabled: true,
        logoutSource: async (ctx: any, form: string) => {
          // oidc-provider generates http:// form actions behind reverse proxy
          const secureForm = form.replace('http://ghostguild.org', 'https://ghostguild.org');
          ctx.body = guildPageShell("Sign Out", `
            <h1>Sign Out</h1>
            <p>Do you want to sign out of your Ghost Guild session?</p>
            <p class="subtext">This will sign you out of the wiki and any other connected services.</p>
            ${secureForm}
            <div class="actions">
              <button class="btn-primary" form="op.logoutForm" type="submit" value="yes" name="logout">Yes, sign me out</button>
              <a class="btn-secondary" href="https://wiki.ghostguild.org">Stay signed in</a>
            </div>
          `, "form#op\\.logoutForm { display: none; }");
        },
        postLogoutSuccessSource: async (ctx: any) => {
          ctx.body = guildPageShell("Signed Out", `
            <h1>Signed Out</h1>
            <p>You have been successfully signed out.</p>
            <div class="actions">
              <a class="btn-primary" href="https://wiki.ghostguild.org">Return to Wiki</a>
            </div>
          `);
        },
      },
    },

    // Mount all OIDC endpoints under /oidc prefix
    routes: {
      authorization: "/oidc/auth",
      backchannel_authentication: "/oidc/backchannel",
      code_verification: "/oidc/device",
      device_authorization: "/oidc/device/auth",
      end_session: "/oidc/session/end",
      introspection: "/oidc/token/introspection",
      jwks: "/oidc/jwks",
      pushed_authorization_request: "/oidc/request",
      registration: "/oidc/reg",
      revocation: "/oidc/token/revocation",
      token: "/oidc/token",
      userinfo: "/oidc/me",
    },

    interactions: {
      url(_ctx: unknown, interaction: { uid: string }) {
        return `/oidc/interaction/${interaction.uid}`;
      },
    },

    renderError: async (ctx: any, out: Record<string, string>, _error: Error) => {
      const details = Object.entries(out)
        .map(([key, value]) => `<strong>${key}:</strong> ${value}`)
        .join("<br>");
      ctx.body = guildPageShell("Something Went Wrong", `
        <h1>Something Went Wrong</h1>
        <p>An error occurred during authentication. Please try again.</p>
        <div class="error-detail">${details}</div>
        <div class="actions">
          <a class="btn-primary" href="https://wiki.ghostguild.org">Return to Wiki</a>
        </div>
      `);
    },

    // Allow Outline to use PKCE but don't require it
    pkce: {
      required: () => false,
    },

    // Skip consent for our first-party Outline client
    loadExistingGrant: async (ctx: any) => {
      const grant = new (ctx.oidc.provider.Grant as any)({
        accountId: ctx.oidc.session!.accountId,
        clientId: ctx.oidc.client!.clientId,
      });
      grant.addOIDCScope("openid profile email");
      await grant.save();
      return grant;
    },
  });

  return _provider;
}
