import { timingSafeEqual } from 'crypto';

import { NextAuthOptions } from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import KeycloakProvider from 'next-auth/providers/keycloak';

import { decodeJwtPayload, extractPortalClaimState } from '@/lib/auth/claims';

/** Read env at runtime (avoids Next.js inlining empty build-time values for Keycloak). */
function env(name: string): string | undefined {
  const v = process.env[name];
  return typeof v === 'string' && v.trim() !== '' ? v.trim() : undefined;
}

function safeEqualStrings(a: string, b: string): boolean {
  const ba = Buffer.from(a, 'utf8');
  const bb = Buffer.from(b, 'utf8');
  if (ba.length !== bb.length) return false;
  return timingSafeEqual(ba, bb);
}

/** Prefer NEXTAUTH_URL (public origin behind NPM) so redirects match the browser host. */
function canonicalAuthBaseUrl(fallback: string): string {
  const raw = env('NEXTAUTH_URL');
  if (!raw) return fallback.replace(/\/$/, '');
  try {
    return new URL(raw).origin;
  } catch {
    return fallback.replace(/\/$/, '');
  }
}

function isPrivateOrLocalHost(hostname: string): boolean {
  if (hostname === 'localhost' || hostname === '127.0.0.1') return true;
  if (hostname.startsWith('192.168.')) return true;
  if (hostname.startsWith('10.')) return true;
  return /^172\.(1[6-9]|2\d|3[0-1])\./.test(hostname);
}

function isKeycloakConfigured(): boolean {
  return Boolean(
    env('KEYCLOAK_URL') && env('KEYCLOAK_CLIENT_ID') && env('KEYCLOAK_CLIENT_SECRET')
  );
}

function isCredentialsFallbackEnabled(): boolean {
  if (env('NODE_ENV') === 'development') return true;
  return env('PORTAL_ENABLE_CREDENTIALS_FALLBACK') === '1';
}

function buildProviders() {
  const providers: NextAuthOptions['providers'] = [];

  if (isKeycloakConfigured()) {
    const keycloakUrl = env('KEYCLOAK_URL')!;
    const realm = env('KEYCLOAK_REALM') || 'master';
    providers.push(
      KeycloakProvider({
        clientId: env('KEYCLOAK_CLIENT_ID')!,
        clientSecret: env('KEYCLOAK_CLIENT_SECRET')!,
        issuer: `${keycloakUrl.replace(/\/$/, '')}/realms/${realm}`,
      })
    );
  }

  const localEmail = env('PORTAL_LOCAL_LOGIN_EMAIL');
  const localPassword = env('PORTAL_LOCAL_LOGIN_PASSWORD');

  if (isCredentialsFallbackEnabled()) {
    providers.push(
      CredentialsProvider({
        id: isKeycloakConfigured() ? 'credentials-fallback' : 'credentials',
        name: 'Credentials',
        credentials: {
          email: { label: 'Email', type: 'email', placeholder: localEmail || 'dev@example.com' },
          password: { label: 'Password', type: 'password' },
        },
        async authorize(credentials) {
          if (env('NODE_ENV') === 'development') {
            return {
              id: 'dev-user',
              email: credentials?.email || 'dev@example.com',
              name: 'Development User',
              role: 'ADMIN',
            };
          }

          if (localEmail && localPassword && credentials?.email && credentials?.password) {
            const emailOk = safeEqualStrings(
              credentials.email.trim().toLowerCase(),
              localEmail.trim().toLowerCase()
            );
            const passOk = safeEqualStrings(credentials.password, localPassword);
            if (emailOk && passOk) {
              return {
                id: 'local-user',
                email: localEmail.trim(),
                name: 'Portal User',
                role: 'ADMIN',
              };
            }
          }

          return null;
        },
      })
    );
  }

  return providers;
}

export const authOptions: NextAuthOptions = {
  providers: buildProviders(),
  callbacks: {
    async redirect({ url, baseUrl }) {
      const canonical = canonicalAuthBaseUrl(baseUrl);
      if (url.startsWith('/')) return `${canonical}${url}`;
      try {
        const target = new URL(url);
        if (target.origin === canonical) return url;
        if (isPrivateOrLocalHost(target.hostname)) {
          return `${canonical}${target.pathname}${target.search}${target.hash}`;
        }
        return canonical;
      } catch {
        return canonical;
      }
    },
    async jwt({ token, account, profile, user }) {
      const accountClaims = decodeJwtPayload(account?.id_token || account?.access_token);
      const profileClaims =
        profile && typeof profile === 'object' ? (profile as Record<string, unknown>) : {};

      if (account) {
        token.accessToken = account.access_token;
        token.refreshToken = account.refresh_token;
        token.idToken = account.id_token;
      }

      if (user) {
        token.id = user.id;
        token.email = user.email;
        token.name = user.name;
        const ur = user as { role?: string };
        if (typeof ur.role === 'string' && ur.role.trim() !== '') {
          const existing = (token.roles as string[] | undefined) || [];
          token.roles = [...new Set([...existing, ur.role])];
        }
      }

      if (profile && 'realm_access' in profile) {
        const realmAccess = profile.realm_access as { roles?: string[] };
        token.roles = realmAccess.roles || [];
      }

      const claimState = extractPortalClaimState(
        accountClaims,
        profileClaims,
        token as Record<string, unknown>
      );
      token.clientId = claimState.clientId || token.clientId;
      token.tenantId = claimState.tenantId || token.tenantId || env('PORTAL_LOCAL_TENANT_ID');
      token.subscriptionId =
        claimState.subscriptionId || token.subscriptionId || env('PORTAL_LOCAL_SUBSCRIPTION_ID');
      token.roles =
        claimState.roles.length > 0 ? claimState.roles : ((token.roles as string[] | undefined) || []);

      return token;
    },
    async session({ session, token }) {
      if (token) {
        session.accessToken = token.accessToken as string;
        session.roles = token.roles as string[];
        session.clientId = token.clientId as string | undefined;
        session.tenantId = token.tenantId as string | undefined;
        session.subscriptionId = token.subscriptionId as string | undefined;
        if (token.id) {
          session.user = {
            ...session.user,
            id: token.id as string,
            email: token.email as string,
            name: token.name as string,
            role:
              Array.isArray(token.roles) && token.roles.length > 0
                ? (token.roles[0] as string)
                : undefined,
          };
        }
      }
      return session;
    },
  },
  pages: {
    error: '/api/auth/error',
  },
  session: {
    strategy: 'jwt',
    maxAge: 24 * 60 * 60,
  },
};
