// @vitest-environment happy-dom
import { describe, expect, it, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, cleanup } from '@testing-library/react';
import { RegisterForm } from '../RegisterForm.js';
import { ForgotPasswordForm } from '../ForgotPasswordForm.js';
import { ResetPasswordForm } from '../ResetPasswordForm.js';
import { VerifyEmailForm } from '../VerifyEmailForm.js';
import { OnboardingShell } from '../OnboardingShell.js';
import { AuthPageLayout } from '../AuthPageLayout.js';
import { PasswordStrengthBar, getPasswordStrength } from '../PasswordStrengthBar.js';

describe('RegisterForm', () => {
  beforeEach(() => cleanup());

  it('renders all fields', () => {
    render(<RegisterForm onSubmit={vi.fn()} />);
    expect(screen.getByTestId('bl-register-name')).toBeDefined();
    expect(screen.getByTestId('bl-register-email')).toBeDefined();
    expect(screen.getByTestId('bl-register-password')).toBeDefined();
    expect(screen.getByTestId('bl-register-confirm')).toBeDefined();
    expect(screen.getByTestId('bl-register-submit')).toBeDefined();
  });

  it('calls onSubmit with name, email, password', () => {
    const onSubmit = vi.fn();
    render(<RegisterForm onSubmit={onSubmit} />);

    fireEvent.change(screen.getByTestId('bl-register-name'), { target: { value: 'Alice' } });
    fireEvent.change(screen.getByTestId('bl-register-email'), {
      target: { value: 'alice@example.com' },
    });
    fireEvent.change(screen.getByTestId('bl-register-password'), {
      target: { value: 'Password1!' },
    });
    fireEvent.change(screen.getByTestId('bl-register-confirm'), {
      target: { value: 'Password1!' },
    });
    fireEvent.submit(screen.getByTestId('bl-register-submit').closest('form')!);

    expect(onSubmit).toHaveBeenCalledWith({
      name: 'Alice',
      email: 'alice@example.com',
      password: 'Password1!',
    });
  });

  it('shows password mismatch error', () => {
    render(<RegisterForm onSubmit={vi.fn()} />);
    fireEvent.change(screen.getByTestId('bl-register-password'), {
      target: { value: 'Password1!' },
    });
    fireEvent.change(screen.getByTestId('bl-register-confirm'), {
      target: { value: 'Different1!' },
    });

    expect(screen.getByTestId('bl-register-mismatch')).toBeDefined();
    expect(screen.getByText('Passwords do not match')).toBeDefined();
  });

  it('displays error message', () => {
    render(<RegisterForm onSubmit={vi.fn()} error="Email already taken" />);
    expect(screen.getByTestId('bl-register-error')).toBeDefined();
    expect(screen.getByText('Email already taken')).toBeDefined();
  });

  it('shows terms checkbox when termsUrl provided', () => {
    render(<RegisterForm onSubmit={vi.fn()} termsUrl="https://example.com/terms" />);
    expect(screen.getByTestId('bl-register-terms')).toBeDefined();
    expect(screen.getByText('Terms of Service')).toBeDefined();
  });

  it('renders switch to login link', () => {
    const onSwitch = vi.fn();
    render(<RegisterForm onSubmit={vi.fn()} onSwitchToLogin={onSwitch} />);
    const link = screen.getByTestId('bl-register-switch-login');
    fireEvent.click(link);
    expect(onSwitch).toHaveBeenCalledOnce();
  });

  it('shows loading state', () => {
    render(<RegisterForm onSubmit={vi.fn()} isLoading />);
    expect(screen.getByText('Creating account...')).toBeDefined();
  });

  it('shows password strength bar when typing', () => {
    render(<RegisterForm onSubmit={vi.fn()} />);
    fireEvent.change(screen.getByTestId('bl-register-password'), { target: { value: 'ab' } });
    expect(screen.getByTestId('bl-password-strength')).toBeDefined();
  });
});

describe('ForgotPasswordForm', () => {
  beforeEach(() => cleanup());

  it('renders email input and submit', () => {
    render(<ForgotPasswordForm onSubmit={vi.fn()} />);
    expect(screen.getByTestId('bl-forgot-email')).toBeDefined();
    expect(screen.getByTestId('bl-forgot-submit')).toBeDefined();
  });

  it('calls onSubmit with email', () => {
    const onSubmit = vi.fn();
    render(<ForgotPasswordForm onSubmit={onSubmit} />);
    fireEvent.change(screen.getByTestId('bl-forgot-email'), {
      target: { value: 'test@example.com' },
    });
    fireEvent.submit(screen.getByTestId('bl-forgot-submit').closest('form')!);
    expect(onSubmit).toHaveBeenCalledWith('test@example.com');
  });

  it('displays error message', () => {
    render(<ForgotPasswordForm onSubmit={vi.fn()} error="Email not found" />);
    expect(screen.getByTestId('bl-forgot-error')).toBeDefined();
  });

  it('displays success message', () => {
    render(<ForgotPasswordForm onSubmit={vi.fn()} success="Check your email" />);
    expect(screen.getByTestId('bl-forgot-success')).toBeDefined();
    expect(screen.getByText('Check your email')).toBeDefined();
  });

  it('renders back button and calls onBack', () => {
    const onBack = vi.fn();
    render(<ForgotPasswordForm onSubmit={vi.fn()} onBack={onBack} />);
    fireEvent.click(screen.getByTestId('bl-forgot-back'));
    expect(onBack).toHaveBeenCalledOnce();
  });

  it('shows loading state', () => {
    render(<ForgotPasswordForm onSubmit={vi.fn()} isLoading />);
    expect(screen.getByText('Sending...')).toBeDefined();
  });
});

describe('ResetPasswordForm', () => {
  beforeEach(() => cleanup());

  it('renders password fields and submit', () => {
    render(<ResetPasswordForm onSubmit={vi.fn()} />);
    expect(screen.getByTestId('bl-reset-password')).toBeDefined();
    expect(screen.getByTestId('bl-reset-confirm')).toBeDefined();
    expect(screen.getByTestId('bl-reset-submit')).toBeDefined();
  });

  it('calls onSubmit with password', () => {
    const onSubmit = vi.fn();
    render(<ResetPasswordForm onSubmit={onSubmit} />);
    fireEvent.change(screen.getByTestId('bl-reset-password'), { target: { value: 'NewPass1!' } });
    fireEvent.change(screen.getByTestId('bl-reset-confirm'), { target: { value: 'NewPass1!' } });
    fireEvent.submit(screen.getByTestId('bl-reset-submit').closest('form')!);
    expect(onSubmit).toHaveBeenCalledWith('NewPass1!');
  });

  it('shows mismatch error', () => {
    render(<ResetPasswordForm onSubmit={vi.fn()} />);
    fireEvent.change(screen.getByTestId('bl-reset-password'), { target: { value: 'NewPass1!' } });
    fireEvent.change(screen.getByTestId('bl-reset-confirm'), { target: { value: 'Different!' } });
    expect(screen.getByTestId('bl-reset-mismatch')).toBeDefined();
  });

  it('displays error and success messages', () => {
    const { rerender } = render(<ResetPasswordForm onSubmit={vi.fn()} error="Token expired" />);
    expect(screen.getByTestId('bl-reset-error')).toBeDefined();

    rerender(<ResetPasswordForm onSubmit={vi.fn()} success="Password updated" />);
    expect(screen.getByTestId('bl-reset-success')).toBeDefined();
  });

  it('shows password strength bar', () => {
    render(<ResetPasswordForm onSubmit={vi.fn()} />);
    fireEvent.change(screen.getByTestId('bl-reset-password'), { target: { value: 'abc' } });
    expect(screen.getByTestId('bl-password-strength')).toBeDefined();
  });
});

describe('VerifyEmailForm', () => {
  beforeEach(() => cleanup());

  it('renders code input and submit', () => {
    render(<VerifyEmailForm onSubmit={vi.fn()} />);
    expect(screen.getByTestId('bl-verify-code')).toBeDefined();
    expect(screen.getByTestId('bl-verify-submit')).toBeDefined();
  });

  it('calls onSubmit with code', () => {
    const onSubmit = vi.fn();
    render(<VerifyEmailForm onSubmit={onSubmit} />);
    fireEvent.change(screen.getByTestId('bl-verify-code'), { target: { value: '123456' } });
    fireEvent.submit(screen.getByTestId('bl-verify-submit').closest('form')!);
    expect(onSubmit).toHaveBeenCalledWith('123456');
  });

  it('displays email address', () => {
    render(<VerifyEmailForm onSubmit={vi.fn()} email="test@example.com" />);
    expect(screen.getByText('test@example.com')).toBeDefined();
  });

  it('renders resend button', () => {
    const onResend = vi.fn();
    render(<VerifyEmailForm onSubmit={vi.fn()} onResend={onResend} />);
    fireEvent.click(screen.getByTestId('bl-verify-resend'));
    expect(onResend).toHaveBeenCalledOnce();
  });

  it('displays error and success messages', () => {
    const { rerender } = render(<VerifyEmailForm onSubmit={vi.fn()} error="Invalid code" />);
    expect(screen.getByTestId('bl-verify-error')).toBeDefined();

    rerender(<VerifyEmailForm onSubmit={vi.fn()} success="Code resent" />);
    expect(screen.getByTestId('bl-verify-success')).toBeDefined();
  });

  it('strips non-numeric characters', () => {
    render(<VerifyEmailForm onSubmit={vi.fn()} />);
    const input = screen.getByTestId('bl-verify-code');
    fireEvent.change(input, { target: { value: 'abc123def456' } });
    expect((input as unknown as { value: string }).value).toBe('123456');
  });
});

describe('OnboardingShell', () => {
  beforeEach(() => cleanup());

  const steps = [
    { key: 'welcome', label: 'Welcome' },
    { key: 'profile', label: 'Profile' },
    { key: 'preferences', label: 'Preferences' },
  ];

  it('renders steps and content', () => {
    render(
      <OnboardingShell
        steps={steps}
        currentStep={0}
        onNext={vi.fn()}
        onBack={vi.fn()}
        onComplete={vi.fn()}
      >
        <div>Step 1 content</div>
      </OnboardingShell>
    );
    expect(screen.getByTestId('bl-onboarding-shell')).toBeDefined();
    expect(screen.getByTestId('bl-onboarding-steps')).toBeDefined();
    expect(screen.getByTestId('bl-onboarding-progress')).toBeDefined();
    expect(screen.getByText('Step 1 content')).toBeDefined();
    expect(screen.getByText('Welcome')).toBeDefined();
    expect(screen.getByText('Profile')).toBeDefined();
  });

  it('disables Back on first step', () => {
    render(
      <OnboardingShell
        steps={steps}
        currentStep={0}
        onNext={vi.fn()}
        onBack={vi.fn()}
        onComplete={vi.fn()}
      >
        <div />
      </OnboardingShell>
    );
    const back = screen.getByTestId('bl-onboarding-back');
    expect(back.getAttribute('disabled')).toBe('');
  });

  it('calls onNext on middle step', () => {
    const onNext = vi.fn();
    render(
      <OnboardingShell
        steps={steps}
        currentStep={1}
        onNext={onNext}
        onBack={vi.fn()}
        onComplete={vi.fn()}
      >
        <div />
      </OnboardingShell>
    );
    fireEvent.click(screen.getByTestId('bl-onboarding-next'));
    expect(onNext).toHaveBeenCalledOnce();
  });

  it('shows Complete on last step and calls onComplete', () => {
    const onComplete = vi.fn();
    render(
      <OnboardingShell
        steps={steps}
        currentStep={2}
        onNext={vi.fn()}
        onBack={vi.fn()}
        onComplete={onComplete}
      >
        <div />
      </OnboardingShell>
    );
    const btn = screen.getByTestId('bl-onboarding-complete');
    expect(btn.textContent).toBe('Complete');
    fireEvent.click(btn);
    expect(onComplete).toHaveBeenCalledOnce();
  });

  it('calls onBack on non-first step', () => {
    const onBack = vi.fn();
    render(
      <OnboardingShell
        steps={steps}
        currentStep={1}
        onNext={vi.fn()}
        onBack={onBack}
        onComplete={vi.fn()}
      >
        <div />
      </OnboardingShell>
    );
    fireEvent.click(screen.getByTestId('bl-onboarding-back'));
    expect(onBack).toHaveBeenCalledOnce();
  });
});

describe('AuthPageLayout', () => {
  beforeEach(() => cleanup());

  it('renders product name and title', () => {
    render(
      <AuthPageLayout productName="TestApp" title="Sign In">
        <div>Form content</div>
      </AuthPageLayout>
    );
    expect(screen.getByTestId('bl-auth-product-name').textContent).toBe('TestApp');
    expect(screen.getByTestId('bl-auth-title').textContent).toBe('Sign In');
    expect(screen.getByText('Form content')).toBeDefined();
  });

  it('renders subtitle when provided', () => {
    render(
      <AuthPageLayout productName="TestApp" title="Sign In" subtitle="Welcome back">
        <div />
      </AuthPageLayout>
    );
    expect(screen.getByTestId('bl-auth-subtitle').textContent).toBe('Welcome back');
  });

  it('renders logo as element', () => {
    render(
      <AuthPageLayout
        productName="TestApp"
        title="Sign In"
        logo={<span data-testid="custom-logo">Logo</span>}
      >
        <div />
      </AuthPageLayout>
    );
    expect(screen.getByTestId('custom-logo')).toBeDefined();
  });

  it('renders footer', () => {
    render(
      <AuthPageLayout productName="TestApp" title="Sign In" footer={<span>Footer text</span>}>
        <div />
      </AuthPageLayout>
    );
    expect(screen.getByTestId('bl-auth-footer')).toBeDefined();
    expect(screen.getByText('Footer text')).toBeDefined();
  });
});

describe('PasswordStrengthBar', () => {
  beforeEach(() => cleanup());

  it('returns null for empty password', () => {
    const { container } = render(<PasswordStrengthBar password="" />);
    expect(container.querySelector('[data-testid="bl-password-strength"]')).toBeNull();
  });

  it('shows Weak for short password', () => {
    render(<PasswordStrengthBar password="ab" />);
    expect(screen.getByTestId('bl-password-strength-label').textContent).toBe('Weak');
  });

  it('shows Strong for complex password', () => {
    render(<PasswordStrengthBar password="MyStr0ng!Pass" />);
    expect(screen.getByTestId('bl-password-strength-label').textContent).toBe('Strong');
  });
});

describe('getPasswordStrength', () => {
  it('returns weak for very short passwords', () => {
    expect(getPasswordStrength('ab')).toBe('weak');
  });

  it('returns fair for medium passwords', () => {
    expect(getPasswordStrength('abcdefgh1')).toBe('fair');
  });

  it('returns good for decent passwords', () => {
    expect(getPasswordStrength('Abcdefgh1')).toBe('good');
  });

  it('returns strong for complex passwords', () => {
    expect(getPasswordStrength('MyStr0ng!Pass')).toBe('strong');
  });
});
