import type { CookieOptions, Response } from 'express';
import { JwtService } from '@nestjs/jwt';
import { resolveRegistrableDomain } from './public-url';

export const FEDCM_SESSION_COOKIE = 'lendry_fedcm_sess';

export interface FedcmSessionPayload {
  sub: string;
  sessionId: string;
  pinVerified: boolean;
}

function cookieSecureEnabled() {
  if (process.env.FEDCM_COOKIE_SECURE === 'true') return true;
  if (process.env.FEDCM_COOKIE_SECURE === 'false') return false;
  const urls = [process.env.PUBLIC_API_URL, process.env.PUBLIC_FRONTEND_URL];
  if (urls.some((url) => url?.trim().startsWith('https://'))) {
    return true;
  }
  return process.env.NODE_ENV === 'production';
}

function cookieMaxAgeSeconds() {
  const parsed = Number(process.env.FEDCM_COOKIE_MAX_AGE ?? 2_592_000);
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 2_592_000;
}

/** Общий домен cookie для api.* / sso.* / apex (FedCM accounts на api-домене). */
export function resolveFedcmCookieDomain(): string | undefined {
  const explicit = process.env.FEDCM_COOKIE_DOMAIN?.trim();
  if (explicit) {
    if (['none', 'off', 'localhost'].includes(explicit.toLowerCase())) {
      return undefined;
    }
    return explicit.startsWith('.') ? explicit : `.${explicit}`;
  }

  for (const raw of [process.env.PUBLIC_API_URL, process.env.PUBLIC_FRONTEND_URL]) {
    const candidate = raw?.trim();
    if (!candidate) continue;
    try {
      const hostname = new URL(candidate).hostname.toLowerCase();
      if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '[::1]') {
        return undefined;
      }
      const apex = resolveRegistrableDomain(hostname);
      if (apex.includes('.')) {
        return `.${apex}`;
      }
    } catch {
      continue;
    }
  }

  return undefined;
}

function buildFedcmCookieOptions(maxAgeMs?: number): CookieOptions {
  const secure = cookieSecureEnabled();
  const options: CookieOptions = {
    httpOnly: true,
    secure,
    sameSite: secure ? 'none' : 'lax',
    path: '/',
    maxAge: maxAgeMs ?? cookieMaxAgeSeconds() * 1000
  };
  const domain = resolveFedcmCookieDomain();
  if (domain) {
    options.domain = domain;
  }
  return options;
}

export async function signFedcmSessionPayload(jwt: JwtService, payload: FedcmSessionPayload) {
  return jwt.signAsync(
    { sub: payload.sub, sessionId: payload.sessionId, pinVerified: payload.pinVerified, typ: 'fedcm_session' },
    {
      secret: process.env.JWT_ACCESS_SECRET ?? 'docker-access-secret',
      expiresIn: cookieMaxAgeSeconds(),
      issuer: 'id.lendry.ru'
    }
  );
}

export async function verifyFedcmSessionToken(jwt: JwtService, token: string): Promise<FedcmSessionPayload | null> {
  try {
    const payload = await jwt.verifyAsync<{ sub: string; sessionId: string; pinVerified?: boolean; typ?: string }>(token, {
      secret: process.env.JWT_ACCESS_SECRET ?? 'docker-access-secret',
      issuer: 'id.lendry.ru'
    });
    if (payload.typ !== 'fedcm_session' || !payload.sub || !payload.sessionId) {
      return null;
    }
    return {
      sub: payload.sub,
      sessionId: payload.sessionId,
      pinVerified: payload.pinVerified !== false
    };
  } catch {
    return null;
  }
}

export async function setFedcmSessionCookie(res: Response, jwt: JwtService, payload: FedcmSessionPayload) {
  if (!payload.sub || !payload.sessionId) {
    return;
  }
  if (res.headersSent) {
    return;
  }

  const value = await signFedcmSessionPayload(jwt, payload);
  if (res.headersSent) {
    return;
  }

  res.cookie(FEDCM_SESSION_COOKIE, value, buildFedcmCookieOptions());
}

export function clearFedcmSessionCookie(res: Response) {
  if (res.headersSent) {
    return;
  }
  res.clearCookie(FEDCM_SESSION_COOKIE, buildFedcmCookieOptions(0));
}

export function readFedcmSessionCookie(cookieHeader?: string): string | null {
  if (!cookieHeader) return null;
  const parts = cookieHeader.split(';');
  for (const part of parts) {
    const [rawKey, ...rawValue] = part.trim().split('=');
    if (rawKey === FEDCM_SESSION_COOKIE) {
      const value = rawValue.join('=').trim();
      return value || null;
    }
  }
  return null;
}

export async function resolveFedcmSessionFromRequest(
  jwt: JwtService,
  cookieHeader?: string
): Promise<FedcmSessionPayload | null> {
  const token = readFedcmSessionCookie(cookieHeader);
  if (!token) return null;
  return verifyFedcmSessionToken(jwt, token);
}

export async function attachFedcmCookieFromAuthResult(
  res: Response,
  jwt: JwtService,
  result: unknown
): Promise<void> {
  if (!result || typeof result !== 'object') return;
  const data = result as {
    accessToken?: string;
    sessionId?: string;
    pinVerified?: boolean;
    requiresPin?: boolean;
    user?: { id?: string };
  };

  if (data.requiresPin || data.pinVerified === false) {
    let userId = data.user?.id;
    if (!userId && data.accessToken) {
      const decoded = jwt.decode(data.accessToken) as { sub?: string } | null;
      userId = decoded?.sub;
    }
    if (userId && data.sessionId) {
      await setFedcmSessionCookie(res, jwt, {
        sub: userId,
        sessionId: data.sessionId,
        pinVerified: false
      });
    } else {
      clearFedcmSessionCookie(res);
    }
    return;
  }

  const userId = data.user?.id;
  if (!userId || !data.sessionId) return;

  await setFedcmSessionCookie(res, jwt, {
    sub: userId,
    sessionId: data.sessionId,
    pinVerified: true
  });
}
