import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

const { mockTrackProductEvent } = vi.hoisted(() => ({ mockTrackProductEvent: vi.fn() }));
vi.mock('@/lib/analytics', () => ({ trackProductEvent: mockTrackProductEvent }));

const mockPut = vi.fn();
const mockPost = vi.fn();
const mockDelete = vi.fn();
const mockSetData = vi.fn();
const mockSetError = vi.fn();
const mockClearErrors = vi.fn();
const mockReset = vi.fn();
let mockIsDirty = false;

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    Head: ({ title }: { title: string }) => <title>{title}</title>,
    useForm: (initialData: Record<string, unknown>) => ({
      data: initialData,
      setData: mockSetData,
      setError: mockSetError,
      clearErrors: mockClearErrors,
      reset: mockReset,
      put: mockPut,
      post: mockPost,
      delete: mockDelete,
      processing: false,
      errors: {},
      get isDirty() {
        return mockIsDirty;
      },
    }),
    usePage: () => ({
      props: {
        flash: {},
      },
    }),
  };
});

vi.mock('@/Layouts/DashboardLayout', () => ({
  default: ({ children }: { children: React.ReactNode }) => (
    <div data-testid="dashboard-layout">{children}</div>
  ),
}));

vi.mock('@/Components/layout/PageHeader', () => ({
  default: ({ title, subtitle }: { title: string; subtitle: string }) => (
    <div data-testid="page-header">
      <h1>{title}</h1>
      <p>{subtitle}</p>
    </div>
  ),
}));

vi.mock('@/Components/BrandingPreview', () => ({
  default: ({
    companyName,
    logoUrl,
    primaryColor,
    secondaryColor,
    removeRankwizBranding,
  }: {
    companyName: string;
    logoUrl: string | null;
    primaryColor: string;
    secondaryColor: string;
    removeRankwizBranding: boolean;
  }) => (
    <div data-testid="branding-preview">
      <span data-testid="preview-company-name">{companyName}</span>
      <span data-testid="preview-logo-url">{logoUrl}</span>
      <span data-testid="preview-primary-color">{primaryColor}</span>
      <span data-testid="preview-secondary-color">{secondaryColor}</span>
      <span data-testid="preview-remove-branding">{String(removeRankwizBranding)}</span>
    </div>
  ),
}));

vi.mock('@/Components/ui/confirm-dialog', () => ({
  ConfirmDialog: ({
    open,
    title,
    description,
  }: {
    open: boolean;
    title: string;
    description: React.ReactNode;
  }) =>
    open ? (
      <div data-testid="confirm-dialog">
        <h2>{title}</h2>
        <div>{description}</div>
      </div>
    ) : null,
}));

import Branding from './Branding';

const noBrandingProps = {
  branding: null,
  reports_cname_target: 'reports.rankwiz.com',
  // WEDGE-GATING-UI: free plan default — toggle visible but disabled.
  can_white_label: false,
};

const withBrandingProps = {
  branding: {
    company_name: 'Acme Corp',
    logo_url: 'https://example.com/logo.png',
    primary_color: '#ff0000',
    secondary_color: '#00ff00',
    remove_rankwiz_branding: true,
    custom_domain: 'reports.acme.com',
  },
  reports_cname_target: 'reports.rankwiz.com',
  // WEDGE-GATING-UI: free plan default for branding props fixture.
  can_white_label: false,
};

describe('Branding Settings Page', () => {
  let addSpy: ReturnType<typeof vi.spyOn>;
  let removeSpy: ReturnType<typeof vi.spyOn>;

  beforeEach(() => {
    vi.clearAllMocks();
    mockIsDirty = false;
    vi.stubGlobal('route', (name: string) => `/mocked/${name}`);
    addSpy = vi.spyOn(window, 'addEventListener');
    removeSpy = vi.spyOn(window, 'removeEventListener');
  });

  afterEach(() => {
    addSpy.mockRestore();
    removeSpy.mockRestore();
  });

  it('fires settings_branding_viewed and feature_used on mount', () => {
    render(<Branding {...noBrandingProps} />);
    expect(mockTrackProductEvent).toHaveBeenCalledWith('settings_branding_viewed');
    expect(mockTrackProductEvent).toHaveBeenCalledWith('feature_used', {
      feature: 'settings_branding',
    });
  });

  it('renders page title "Branding"', () => {
    render(<Branding {...noBrandingProps} />);

    expect(document.title).toBe('Branding');
  });

  it('renders heading "Branding"', () => {
    render(<Branding {...noBrandingProps} />);

    expect(screen.getByRole('heading', { name: /^branding$/i })).toBeInTheDocument();
  });

  it('renders subtitle about customizing reports', () => {
    render(<Branding {...noBrandingProps} />);

    // R6UXA-009: rewritten away from agency/client framing to personal report branding
    expect(
      screen.getByText(/add your own logo and colors to personalize your reports and exports/i),
    ).toBeInTheDocument();
  });

  it('does not render agency/client framing copy (R6UXA-009 regression guard)', () => {
    render(<Branding {...noBrandingProps} />);

    // These strings were present before R6UXA-009 — their absence is the regression guard.
    expect(
      screen.queryByText(/professional client deliverables/i),
    ).not.toBeInTheDocument();
    expect(
      screen.queryByText(/client-facing materials/i),
    ).not.toBeInTheDocument();
    expect(screen.queryByText('Company Name')).not.toBeInTheDocument();
    expect(
      screen.queryByText(/company logo/i),
    ).not.toBeInTheDocument();
    expect(screen.queryByText('Branding Settings')).not.toBeInTheDocument();
  });

  it('shows "Upload Logo" label when no logo exists', () => {
    render(<Branding {...noBrandingProps} />);

    expect(screen.getByText('Upload Logo')).toBeInTheDocument();
  });

  it('shows "Update Logo" label when logo exists', () => {
    render(<Branding {...withBrandingProps} />);

    expect(screen.getByText('Update Logo')).toBeInTheDocument();
  });

  it('shows logo preview when logo exists', () => {
    render(<Branding {...withBrandingProps} />);

    const logoImg = screen.getByAltText('Company logo preview');
    expect(logoImg).toBeInTheDocument();
    expect(logoImg).toHaveAttribute('src', 'https://example.com/logo.png');
  });

  it('shows "Remove Logo" button when logo exists', () => {
    render(<Branding {...withBrandingProps} />);

    expect(screen.getByRole('button', { name: /remove logo/i })).toBeInTheDocument();
  });

  it('does not show "Remove Logo" button when no logo exists', () => {
    render(<Branding {...noBrandingProps} />);

    expect(screen.queryByRole('button', { name: /remove logo/i })).not.toBeInTheDocument();
  });

  it('renders file input for logo upload', () => {
    render(<Branding {...noBrandingProps} />);

    const fileInput = screen.getByLabelText(/upload logo/i);
    expect(fileInput).toBeInTheDocument();
    expect(fileInput).toHaveAttribute('type', 'file');
    expect(fileInput).toHaveAttribute('accept', 'image/png,image/jpeg,image/jpg,image/svg+xml');
  });

  it('shows logo file size and type requirements', () => {
    render(<Branding {...noBrandingProps} />);

    expect(screen.getByText(/png, jpg, or svg\. max file size: 2mb\./i)).toBeInTheDocument();
  });

  it('renders display name input with default value', () => {
    render(<Branding {...noBrandingProps} />);

    // R6UXA-009: label changed from "Company Name" → "Display Name" to avoid agency framing.
    // Placeholder changed from "Your Company Name" → "Your name or blog name".
    const displayNameInput = screen.getByLabelText(/display name/i);
    expect(displayNameInput).toBeInTheDocument();
    expect(displayNameInput).toHaveAttribute('placeholder', 'Your name or blog name');
  });

  it('renders display name input with existing value', () => {
    render(<Branding {...withBrandingProps} />);

    // R6UXA-009: label renamed from "Company Name" → "Display Name"
    const displayNameInput = screen.getByLabelText(/display name/i) as HTMLInputElement;
    expect(displayNameInput.value).toBe('Acme Corp');
  });

  it('renders primary color input', () => {
    render(<Branding {...noBrandingProps} />);

    const colorInputs = screen.getAllByDisplayValue('#7C3AED');
    expect(colorInputs.length).toBeGreaterThan(0);
  });

  it('renders secondary color input', () => {
    render(<Branding {...noBrandingProps} />);

    const colorInputs = screen.getAllByDisplayValue('#5B21B6');
    expect(colorInputs.length).toBeGreaterThan(0);
  });

  it('renders primary and secondary color with existing values', () => {
    render(<Branding {...withBrandingProps} />);

    const primaryInputs = screen.getAllByDisplayValue('#ff0000');
    const secondaryInputs = screen.getAllByDisplayValue('#00ff00');
    expect(primaryInputs.length).toBeGreaterThan(0);
    expect(secondaryInputs.length).toBeGreaterThan(0);
  });

  it('renders remove RankWizAI branding checkbox', () => {
    render(<Branding {...noBrandingProps} />);

    expect(
      screen.getByRole('checkbox', { name: /remove rankwizai branding from reports/i }),
    ).toBeInTheDocument();
  });

  it('shows checkbox checked when remove_rankwiz_branding is true', () => {
    render(<Branding {...withBrandingProps} />);

    const checkbox = screen.getByRole('checkbox', {
      name: /remove rankwizai branding from reports/i,
    });
    expect(checkbox).toBeChecked();
  });

  it('renders custom domain input', () => {
    render(<Branding {...noBrandingProps} />);

    const domainInput = screen.getByLabelText(/custom domain/i);
    expect(domainInput).toBeInTheDocument();
    expect(domainInput).toHaveAttribute('placeholder', 'reports.yourcompany.com');
  });

  it('renders custom domain input with existing value', () => {
    render(<Branding {...withBrandingProps} />);

    const domainInput = screen.getByLabelText(/custom domain/i) as HTMLInputElement;
    expect(domainInput.value).toBe('reports.acme.com');
  });

  it('shows CNAME configuration instructions', () => {
    render(<Branding {...noBrandingProps} />);

    expect(
      screen.getByText(/point your domain's cname record to reports\.rankwiz\.com/i),
    ).toBeInTheDocument();
  });

  it('renders "Save Branding Settings" button', () => {
    render(<Branding {...noBrandingProps} />);

    expect(screen.getByRole('button', { name: /save branding settings/i })).toBeInTheDocument();
  });

  it('renders BrandingPreview component', () => {
    render(<Branding {...noBrandingProps} />);

    expect(screen.getByTestId('branding-preview')).toBeInTheDocument();
  });

  it('passes correct props to BrandingPreview with default values', () => {
    render(<Branding {...noBrandingProps} />);

    expect(screen.getByTestId('preview-company-name')).toHaveTextContent('');
    expect(screen.getByTestId('preview-logo-url')).toHaveTextContent('');
    expect(screen.getByTestId('preview-primary-color')).toHaveTextContent('#7C3AED');
    expect(screen.getByTestId('preview-secondary-color')).toHaveTextContent('#5B21B6');
    expect(screen.getByTestId('preview-remove-branding')).toHaveTextContent('false');
  });

  it('passes correct props to BrandingPreview with existing values', () => {
    render(<Branding {...withBrandingProps} />);

    expect(screen.getByTestId('preview-company-name')).toHaveTextContent('Acme Corp');
    expect(screen.getByTestId('preview-logo-url')).toHaveTextContent(
      'https://example.com/logo.png',
    );
    expect(screen.getByTestId('preview-primary-color')).toHaveTextContent('#ff0000');
    expect(screen.getByTestId('preview-secondary-color')).toHaveTextContent('#00ff00');
    expect(screen.getByTestId('preview-remove-branding')).toHaveTextContent('true');
  });

  it('shows both Logo and Report Styling cards', () => {
    render(<Branding {...noBrandingProps} />);

    // R6UXA-009: "Branding Settings" card renamed to "Report Styling"
    expect(screen.getByText('Logo')).toBeInTheDocument();
    expect(screen.getByText('Report Styling')).toBeInTheDocument();
  });

  it('shows logo card description', () => {
    render(<Branding {...noBrandingProps} />);

    // R6UXA-009: "company logo" → "a logo" (removed agency framing)
    expect(
      screen.getByText(/upload a logo to appear on your reports and exports/i),
    ).toBeInTheDocument();
  });

  it('shows branding settings card description', () => {
    render(<Branding {...noBrandingProps} />);

    // R6UXA-009: removed "client-facing materials" framing
    expect(
      screen.getByText(/customize the colors and name that appear on your reports and exports/i),
    ).toBeInTheDocument();
  });

  describe('unsaved changes warning', () => {
    it('registers beforeunload listener', () => {
      render(<Branding {...noBrandingProps} />);

      expect(addSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function));
    });

    it('removes listener on unmount', () => {
      const { unmount } = render(<Branding {...noBrandingProps} />);

      unmount();

      expect(removeSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function));
    });

    it('prevents unload when form is dirty', () => {
      mockIsDirty = true;
      render(<Branding {...noBrandingProps} />);

      const handler = addSpy.mock.calls.find(
        ([event]: [string, ...unknown[]]) => event === 'beforeunload',
      )![1] as EventListener;

      const event = new Event('beforeunload') as BeforeUnloadEvent;
      const preventSpy = vi.spyOn(event, 'preventDefault');

      handler(event);

      expect(preventSpy).toHaveBeenCalled();
    });

    it('does not prevent unload when form is not dirty', () => {
      mockIsDirty = false;
      render(<Branding {...noBrandingProps} />);

      const handler = addSpy.mock.calls.find(
        ([event]: [string, ...unknown[]]) => event === 'beforeunload',
      )![1] as EventListener;

      const event = new Event('beforeunload') as BeforeUnloadEvent;
      const preventSpy = vi.spyOn(event, 'preventDefault');

      handler(event);

      expect(preventSpy).not.toHaveBeenCalled();
    });
  });

  // ── WEDGE-GATING-UI: white-label toggle capability gate ──────────────────
  describe('White-label toggle capability gate (WEDGE-GATING-UI)', () => {
    it('disables the remove-branding checkbox when can_white_label is false (free user)', () => {
      render(<Branding {...noBrandingProps} can_white_label={false} />);

      const checkbox = screen.getByRole('checkbox', {
        name: /remove rankwizai branding from reports/i,
      });
      expect(checkbox).toBeDisabled();
    });

    it('renders aria-disabled on the checkbox when can_white_label is false', () => {
      render(<Branding {...noBrandingProps} can_white_label={false} />);

      const checkbox = screen.getByRole('checkbox', {
        name: /remove rankwizai branding from reports/i,
      });
      expect(checkbox).toHaveAttribute('aria-disabled', 'true');
    });

    it('shows upgrade invite copy when can_white_label is false', () => {
      render(<Branding {...noBrandingProps} can_white_label={false} />);

      expect(screen.getByText(/remove on a paid plan/i)).toBeInTheDocument();
    });

    it('shows lock icon aria-hidden when can_white_label is false', () => {
      const { container } = render(<Branding {...noBrandingProps} can_white_label={false} />);

      // The upgrade paragraph has id "remove-branding-upgrade" and the Lock SVG inside it
      const upgradeEl = container.querySelector('#remove-branding-upgrade');
      expect(upgradeEl).toBeInTheDocument();
    });

    it('enables the checkbox when can_white_label is true (paid user)', () => {
      render(<Branding {...noBrandingProps} can_white_label={true} />);

      const checkbox = screen.getByRole('checkbox', {
        name: /remove rankwizai branding from reports/i,
      });
      expect(checkbox).not.toBeDisabled();
    });

    it('does not render upgrade invite copy when can_white_label is true', () => {
      render(<Branding {...noBrandingProps} can_white_label={true} />);

      expect(screen.queryByText(/remove on a paid plan/i)).not.toBeInTheDocument();
    });

    it('does not render upgrade aria-described-by for checkbox when paid', () => {
      render(<Branding {...noBrandingProps} can_white_label={true} />);

      const checkbox = screen.getByRole('checkbox', {
        name: /remove rankwizai branding from reports/i,
      });
      expect(checkbox).not.toHaveAttribute('aria-describedby', 'remove-branding-upgrade');
    });
  });
});
