import { Page, expect } from '@playwright/test';
import { BasePage } from './BasePage';
import { TestUser } from '../types';

/**
 * Login Page Object
 * Handles all interactions on the /login route
 */
export class LoginPage extends BasePage {
  // Locators (all using user-facing patterns, no raw CSS selectors)
  private readonly usernameInput = () => this.page.getByLabel(/username|user/i).first();
  private readonly passwordInput = () => this.page.getByLabel(/password/i).first();
  // Submit button - matches login "Enter Middle-earth", signup "Create account", or loading "Opening the Gates..."
  private readonly submitButton = () => this.page.getByRole('button', { name: /enter middle-earth|create account|opening the gates/i }).first();
  // Error alert: Alert component renders as motion.div with class text-red-600; title h4 is "The Gate Remains Closed"
  private readonly errorAlertTitle = () => this.page.locator('h4').filter({ hasText: /The Gate Remains Closed/i }).first();
  private readonly errorAlertContainer = () => this.page.locator('[class*="text-red"]').filter({ hasText: 'The Gate Remains Closed' }).first();
  private readonly hintText = () => this.page.getByText(/default password/i).first();
  private readonly signupButton = () => this.page.getByRole('button', { name: /sign up|register/i }).first();

  async goto(): Promise<void> {
    await super.goto('/login');
  }

  /**
   * Login with username and password
   */
  async login(username: string, password: string): Promise<void> {
    await this.goto();
    await this.usernameInput().fill(username);
    await this.passwordInput().fill(password);
    await this.submitButton().click();
  }

  /**
   * Login with a TestUser object
   */
  async loginWithUser(user: TestUser): Promise<void> {
    await this.login(user.username, user.password);
  }

  /**
   * Wait for successful login redirect to dashboard
   * Uses active polling to detect dashboard presence and readiness
   */
  async waitForDashboard(timeout: number = 30000): Promise<void> {
    // Active polling: wait for dashboard URL and content
    await this.pollUntil(
      async () => {
        try {
          const url = this.page.url();
          if (!url.includes('/dashboard')) {
            return false;
          }

          // Also check that some dashboard content is visible
          const dashboardContent = this.page.locator('.dashboard, [data-testid="mission-briefing"], h1:has-text("Mission")');
          const isVisible = await dashboardContent.isVisible().catch(() => false);
          return isVisible;
        } catch {
          return false;
        }
      },
      timeout,
      500, // Poll every 500ms
    );
  }

  /**
   * Wait for redirect to a specific path
   */
  async waitForRedirect(path: string, timeout: number = 15000): Promise<void> {
    await this.page.waitForURL(`**${path}`, { timeout });
  }

  /**
   * Check if error message is visible
   */
  async isErrorVisible(): Promise<boolean> {
    return this.errorAlertTitle().isVisible({ timeout: 5000 }).catch(() => false);
  }

  /**
   * Get error message text
   */
  async getErrorText(): Promise<string> {
    try {
      return await this.errorAlertContainer().textContent({ timeout: 3000 }) || '';
    } catch {
      return '';
    }
  }

  /**
   * Verify error message contains specific text
   */
  async expectError(text: string | RegExp): Promise<void> {
    await expect(this.errorAlertContainer()).toContainText(text);
  }

  /**
   * Check if hint text is visible
   */
  async isHintVisible(): Promise<boolean> {
    return this.hintText().isVisible({ timeout: 3000 }).catch(() => false);
  }

  /**
   * Swap to signup mode
   */
  async goToSignup(): Promise<void> {
    await this.signupButton().click();
  }

  /**
   * Sign up with email and password
   */
  async signup(username: string, password: string, email?: string): Promise<void> {
    await this.goto();
    await this.goToSignup();
    await this.usernameInput().fill(username);
    if (email) {
      await this.page.getByLabel(/email/i).fill(email);
    }
    await this.passwordInput().fill(password);
    await this.submitButton().click();
  }
}
