import type { Provider } from '@supabase/supabase-js';

import authConfig from './auth.config';

type SupabaseExternalProvider = Provider | 'email';
interface SupabaseAuthSettings {
  external: Record<SupabaseExternalProvider, boolean>;
  disable_signup: boolean;
  mailer_autoconfirm: boolean;
}

export class AuthProvidersService {
  private supabaseUrl: string;
  private cache: Map<
    string,
    { data: SupabaseAuthSettings; timestamp: number }
  > = new Map();
  private readonly CACHE_TTL = 5 * 60 * 1000; // 5 minutes

  constructor(supabaseUrl: string) {
    this.supabaseUrl = supabaseUrl;
  }

  async fetchAuthSettings(): Promise<SupabaseAuthSettings | null> {
    try {
      const cacheKey = 'auth-settings';
      const cached = this.cache.get(cacheKey);

      if (cached && Date.now() - cached.timestamp < this.CACHE_TTL) {
        return cached.data;
      }

      const anonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
      if (!anonKey) {
        throw new Error('NEXT_PUBLIC_SUPABASE_ANON_KEY is required');
      }

      const response = await fetch(
        `${this.supabaseUrl}/auth/v1/settings?apikey=${anonKey}`,
        {
          method: 'GET',
          headers: {
            'Content-Type': 'application/json',
          },
        },
      );

      if (!response.ok) {
        console.warn(
          'Failed to fetch auth settings from Supabase:',
          response.status,
        );
        return null;
      }

      const settings: SupabaseAuthSettings = await response.json();

      this.cache.set(cacheKey, { data: settings, timestamp: Date.now() });

      return settings;
    } catch (error) {
      console.warn('Error fetching auth settings from Supabase:', error);
      return null;
    }
  }

  isPasswordEnabled({
    settings,
  }: {
    settings: SupabaseAuthSettings | null;
  }): boolean {
    if (settings) {
      return settings.external.email === true && !settings.disable_signup;
    }

    return process.env.NEXT_PUBLIC_AUTH_PASSWORD === 'true';
  }

  isMailerAutoconfirmEnabled({
    settings,
  }: {
    settings: SupabaseAuthSettings | null;
  }): boolean {
    return settings?.mailer_autoconfirm === true;
  }

  isMagicLinkEnabled(): boolean {
    return process.env.NEXT_PUBLIC_AUTH_MAGIC_LINK === 'true';
  }

  isOAuthProviderEnabled({
    provider,
    settings,
  }: {
    provider: SupabaseExternalProvider;
    settings: SupabaseAuthSettings | null;
  }): boolean {
    if (settings && settings.external) {
      return settings.external[provider] === true;
    }

    return false;
  }

  getEnabledOAuthProviders({
    settings,
  }: {
    settings: SupabaseAuthSettings | null;
  }): SupabaseExternalProvider[] {
    const enabledProviders: SupabaseExternalProvider[] = [];

    if (settings && settings.external) {
      for (const [providerName, isEnabled] of Object.entries(
        settings.external,
      )) {
        if (isEnabled && providerName !== 'email') {
          enabledProviders.push(providerName as SupabaseExternalProvider);
        }
      }
      return enabledProviders;
    }

    const potentialProviders: SupabaseExternalProvider[] = ['keycloak'];
    const enabledFallback: SupabaseExternalProvider[] = [];

    for (const provider of potentialProviders) {
      if (
        provider !== 'email' &&
        this.isOAuthProviderEnabled({ provider, settings })
      ) {
        enabledFallback.push(provider);
      }
    }

    return enabledFallback;
  }

  async getAuthConfig() {
    const settings = await this.fetchAuthSettings();
    const [
      passwordEnabled,
      magicLinkEnabled,
      oAuthProviders,
      isMailerAutoconfirmEnabled,
    ] = await Promise.all([
      this.isPasswordEnabled({ settings }),
      this.isMagicLinkEnabled(),
      this.getEnabledOAuthProviders({ settings }),
      this.isMailerAutoconfirmEnabled({ settings }),
    ]);

    return {
      providers: {
        password: passwordEnabled,
        magicLink: magicLinkEnabled,
        oAuth: oAuthProviders,
      },
      displayTermsCheckbox: authConfig.displayTermsCheckbox,
      isMailerAutoconfirmEnabled,
    };
  }

  clearCache(): void {
    this.cache.clear();
  }
}

export function createAuthProvidersService(): AuthProvidersService {
  const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;

  if (!supabaseUrl) {
    throw new Error('NEXT_PUBLIC_SUPABASE_URL is required');
  }

  return new AuthProvidersService(supabaseUrl);
}
