import type { Page } from 'playwright';
import type { LoggerPort } from 'apps/companion/main/automation/application/ports/LoggerPort';
import type { IPlaywrightAuthFlow } from './PlaywrightAuthFlow';
import { IRACING_URLS, IRACING_SELECTORS, IRACING_TIMEOUTS } from '../dom/IRacingSelectors';
import { AuthenticationGuard } from './AuthenticationGuard';

export class IRacingPlaywrightAuthFlow implements IPlaywrightAuthFlow {
  constructor(private readonly logger?: LoggerPort) {}

  getLoginUrl(): string {
    return IRACING_URLS.login;
  }

  getPostLoginLandingUrl(): string {
    return IRACING_URLS.hostedSessions;
  }

  isLoginUrl(url: string): boolean {
    const lower = url.toLowerCase();
    return (
      lower.includes('oauth.iracing.com') ||
      lower.includes('/membersite/login') ||
      lower.includes('/login.jsp') ||
      lower.includes('/login')
    );
  }

  isAuthenticatedUrl(url: string): boolean {
    const lower = url.toLowerCase();
    return (
      lower.includes('/web/racing/hosted') ||
      lower.includes('/membersite/member') ||
      lower.includes('members-ng.iracing.com') ||
      lower.startsWith(IRACING_URLS.hostedSessions.toLowerCase()) ||
      lower.startsWith(IRACING_URLS.home.toLowerCase())
    );
  }

  isLoginSuccessUrl(url: string): boolean {
    return this.isAuthenticatedUrl(url) && !this.isLoginUrl(url);
  }

  async detectAuthenticatedUi(page: Page): Promise<boolean> {
    const authSelectors = [
      IRACING_SELECTORS.hostedRacing.createRaceButton,
      '[aria-label*="user menu" i]',
      '[aria-label*="account menu" i]',
      '.user-menu',
      '.account-menu',
      'nav a[href*="/membersite"]',
      'nav a[href*="/members"]',
    ];

    for (const selector of authSelectors) {
      try {
        const element = page.locator(selector).first();
        const isVisible = await element.isVisible().catch(() => false);
        if (isVisible) {
          this.logger?.info?.('Authenticated UI detected', { selector });
          return true;
        }
      } catch {
        // Ignore selector errors, try next selector
      }
    }

    return false;
  }

  async detectLoginUi(page: Page): Promise<boolean> {
    const guard = new AuthenticationGuard(page, this.logger);
    return guard.checkForLoginUI();
  }

  async navigateToAuthenticatedArea(page: Page): Promise<void> {
    await page.goto(this.getPostLoginLandingUrl(), {
      waitUntil: 'domcontentloaded',
      timeout: IRACING_TIMEOUTS.navigation,
    });
  }

  async waitForPostLoginRedirect(page: Page, timeoutMs: number): Promise<boolean> {
    const start = Date.now();
    this.logger?.info?.('Waiting for post-login redirect', { timeoutMs });

    while (Date.now() - start < timeoutMs) {
      try {
        if (page.isClosed()) {
          this.logger?.warn?.('Page closed while waiting for post-login redirect');
          return false;
        }

        const url = page.url();

        if (this.isLoginSuccessUrl(url)) {
          this.logger?.info?.('Login success detected by URL', { url });
          return true;
        }

        // Fallback: detect authenticated UI even if URL is not the canonical one
        const hasAuthUi = await this.detectAuthenticatedUi(page);
        if (hasAuthUi) {
          this.logger?.info?.('Login success detected by authenticated UI', { url });
          return true;
        }

        await new Promise((resolve) => setTimeout(resolve, 1000));
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error);
        this.logger?.debug?.('Error while waiting for post-login redirect', { error: message });

        if (page.isClosed()) {
          return false;
        }

        await new Promise((resolve) => setTimeout(resolve, 500));
      }
    }

    this.logger?.warn?.('Post-login redirect wait timed out', { timeoutMs });
    return false;
  }
}