import { firstValueFrom } from 'rxjs';
import { CoreGrpcService } from '../core-grpc.service';
import { normalizePublicBaseUrl, pickBestPublicBase, isInternalHostname, normalizeDedicatedApiIssuer } from './public-url';

function normalizeBaseUrl(url: string) {
  return normalizePublicBaseUrl(url);
}

function appendIdpApiPath(base: string) {
  const normalized = normalizeBaseUrl(base);
  return normalized.endsWith('/idp-api') ? normalized : `${normalized}/idp-api`;
}

function sanitizeStoredPublicUrl(url: string) {
  const normalized = normalizeBaseUrl(url);
  try {
    if (isInternalHostname(new URL(normalized).hostname)) {
      return undefined;
    }
  } catch {
    return normalized;
  }
  return normalized;
}

async function resolveProjectDomainUrl(core: CoreGrpcService, withIdpApiPath = false): Promise<string | undefined> {
  try {
    const response = (await firstValueFrom(core.settings.GetSetting({ key: 'PROJECT_DOMAIN' }))) as { value?: string };
    const domain = response.value?.trim();
    if (!domain) {
      return undefined;
    }
    if (domain.startsWith('http://') || domain.startsWith('https://')) {
      return withIdpApiPath ? appendIdpApiPath(domain) : normalizeBaseUrl(domain);
    }
    const base = `https://${domain.replace(/^\/+/, '')}`;
    return withIdpApiPath ? appendIdpApiPath(base) : base;
  } catch {
    return undefined;
  }
}

export async function resolveOAuthIssuer(core: CoreGrpcService, fallback = 'http://localhost:3000'): Promise<string> {
  const envIssuer = sanitizeStoredPublicUrl(process.env.PUBLIC_API_URL?.trim() ?? '');
  let fromDb: string | undefined;
  try {
    const response = (await firstValueFrom(core.settings.GetSetting({ key: 'PUBLIC_API_URL' }))) as { value?: string };
    fromDb = sanitizeStoredPublicUrl(response.value ?? '');
  } catch {
    // fallback ниже
  }

  const fromDomain = await resolveProjectDomainUrl(core, true);
  // env первым: в Docker .env перекрывает seed localhost в БД после первого деплоя.
  const picked = pickBestPublicBase([envIssuer, fromDb, fromDomain], fallback);
  return normalizeDedicatedApiIssuer(picked);
}

export async function resolveFrontendUrl(core: CoreGrpcService, fallback = 'http://localhost:3002'): Promise<string> {
  const envFrontend = sanitizeStoredPublicUrl(process.env.PUBLIC_FRONTEND_URL?.trim() ?? '');
  let fromDb: string | undefined;
  try {
    const response = (await firstValueFrom(core.settings.GetSetting({ key: 'PUBLIC_FRONTEND_URL' }))) as { value?: string };
    fromDb = sanitizeStoredPublicUrl(response.value ?? '');
  } catch {
    // fallback ниже
  }

  const fromDomain = await resolveProjectDomainUrl(core, false);
  return pickBestPublicBase([envFrontend, fromDb, fromDomain], fallback);
}

export async function resolveProjectDomainFrontendUrl(core: CoreGrpcService): Promise<string | undefined> {
  return resolveProjectDomainUrl(core, false);
}

export async function resolveProjectDomainApiUrl(core: CoreGrpcService): Promise<string | undefined> {
  return resolveProjectDomainUrl(core, true);
}

export function buildOpenIdConfiguration(issuer: string) {
  const base = normalizeBaseUrl(issuer);
  return {
    issuer: base,
    authorization_endpoint: `${base}/oauth/authorize`,
    token_endpoint: `${base}/oauth/token`,
    userinfo_endpoint: `${base}/oauth/userinfo`,
    jwks_uri: `${base}/.well-known/jwks.json`,
    response_types_supported: ['code'],
    subject_types_supported: ['public'],
    id_token_signing_alg_values_supported: ['HS256'],
    scopes_supported: ['openid', 'profile', 'email', 'phone', 'address', 'documents'],
    token_endpoint_auth_methods_supported: ['client_secret_post', 'client_secret_basic'],
    grant_types_supported: ['authorization_code', 'refresh_token'],
    code_challenge_methods_supported: ['S256', 'plain'],
    claims_supported: [
      'sub',
      'iss',
      'aud',
      'iat',
      'exp',
      'auth_time',
      'nonce',
      'email',
      'email_verified',
      'phone_number',
      'phone_number_verified',
      'name',
      'preferred_username',
      'picture'
    ]
  };
}
