import { describe, expect, it } from 'vitest';

import type { HubReadiness, SiteSummary } from '@/types';

import {
  CANONICAL_ROUTE_LABELS,
  getHubNavStructure,
  getRouteCanonicalLabel,
} from './site-navigation';

/**
 * R6IA-002 / R6IA-003 — Nav consolidation + route-label drift tests.
 *
 * Verifies the consolidated solo-persona IA:
 *   - Opportunities hub has zero sidebar children (collapsed to hub-leaf).
 *   - Overview hub has exactly 2 sidebar children (Analyze + ROI).
 *   - Total sidebar destinations ≤ 14 for a free-plan user.
 *   - Every route with a nav entry maps to its canonical label.
 *   - Alert Subscriptions routes to notification-settings.show (not alerts.index).
 *   - CANONICAL_ROUTE_LABELS covers the known site-scoped routes.
 */

const site: SiteSummary = {
  id: 1,
  name: 'Example',
  domain: 'example.com',
  has_gsc: true,
  gsc_synced: true,
  has_analysis: true,
} as unknown as SiteSummary;

const readiness: HubReadiness | null = null;

/**
 * R6UXS-004: Opportunities + Content hubs merged into 'recover'.
 * The 'opportunities' key is no longer returned by getHubNavStructure.
 * Tests updated from "Opportunities hub has 0 children" to "Recover hub
 * has the expected children (4 primary children)."
 */
describe('R6IA-002 / R6UXS-004 — Consolidated IA: Recover hub (merged Opportunities + Content)', () => {
  it('Recover hub exists with key "recover"', () => {
    const hubs = getHubNavStructure(site, readiness);
    const recover = hubs.find((h) => h.key === 'recover');
    expect(recover).toBeDefined();
  });

  it('Recover hub routes to sites.opportunities.index', () => {
    const hubs = getHubNavStructure(site, readiness);
    const recover = hubs.find((h) => h.key === 'recover');
    expect(recover!.routeName).toBe('sites.opportunities.index');
  });

  it('Recover hub has the expected sidebar children (content-inventory, content-briefs, batch-ai, reports)', () => {
    const hubs = getHubNavStructure(site, readiness);
    const recover = hubs.find((h) => h.key === 'recover');
    expect(recover).toBeDefined();
    const keys = recover!.children.map((c) => c.key);
    expect(keys).toContain('content-inventory');
    expect(keys).toContain('content-briefs');
    expect(keys).toContain('batch-ai');
    expect(keys).toContain('reports');
  });

  it('Opportunities hub key is no longer returned (replaced by recover)', () => {
    const hubs = getHubNavStructure(site, readiness);
    const opps = hubs.find((h) => h.key === 'opportunities');
    expect(opps).toBeUndefined();
  });

  it('Content hub key is no longer returned (merged into recover)', () => {
    const hubs = getHubNavStructure(site, readiness);
    const content = hubs.find((h) => h.key === 'content');
    expect(content).toBeUndefined();
  });
});

describe('R6IA-002 — Consolidated IA: Overview hub trimmed to primary children', () => {
  it('Overview hub has exactly 2 sidebar children: Analyze and ROI', () => {
    const hubs = getHubNavStructure(site, readiness);
    const overview = hubs.find((h) => h.key === 'overview');
    expect(overview).toBeDefined();
    expect(overview!.children).toHaveLength(2);
    const keys = overview!.children.map((c) => c.key);
    expect(keys).toContain('analyze');
    expect(keys).toContain('roi');
  });

  it('Pipeline is NOT a sidebar child (demoted to Overview hub page)', () => {
    const hubs = getHubNavStructure(site, readiness);
    const overview = hubs.find((h) => h.key === 'overview');
    const keys = overview!.children.map((c) => c.key);
    expect(keys).not.toContain('pipeline');
  });

  it('Traffic Alerts is NOT a sidebar child (demoted to Overview hub page)', () => {
    const hubs = getHubNavStructure(site, readiness);
    const overview = hubs.find((h) => h.key === 'overview');
    const keys = overview!.children.map((c) => c.key);
    expect(keys).not.toContain('alerts');
  });
});

describe('R6IA-002 / R6UXS-004 — Consolidated IA: total sidebar destinations', () => {
  it('total sidebar children ≤ 14 for a free-plan user with full access', () => {
    const hubs = getHubNavStructure(site, readiness, 'free');
    const totalChildren = hubs.reduce((n, h) => n + h.children.length, 0);
    // R6UXS-004: Overview(2) + Recover(4) + Perf(2) + Settings(3) = 11 (merged hubs, down from 12)
    expect(totalChildren).toBeLessThanOrEqual(14);
  });

  it('has exactly 4 hubs (R6UXS-004: Opportunities + Content merged into Recover)', () => {
    const hubs = getHubNavStructure(site, readiness);
    // 4 hubs: Overview · Recover · Performance · Settings
    expect(hubs).toHaveLength(4);
  });
});

describe('R6IA-003 — Route-label canonical map', () => {
  it('CANONICAL_ROUTE_LABELS exports a non-empty record', () => {
    expect(Object.keys(CANONICAL_ROUTE_LABELS).length).toBeGreaterThan(10);
  });

  it('getRouteCanonicalLabel returns the label for known routes', () => {
    expect(getRouteCanonicalLabel('opportunity-map.index')).toBe('Action Queue');
    expect(getRouteCanonicalLabel('alerts.index')).toBe('Traffic Alerts');
    expect(getRouteCanonicalLabel('notification-settings.show')).toBe('Alert Subscriptions');
    expect(getRouteCanonicalLabel('sites.roi.index')).toBe('ROI');
  });

  it('getRouteCanonicalLabel falls back to the route name for unknown routes', () => {
    expect(getRouteCanonicalLabel('some.unknown.route')).toBe('some.unknown.route');
  });

  it('OpportunityMap route has canonical label "Action Queue" (not "OpportunityMap" or "Opportunity Map")', () => {
    expect(CANONICAL_ROUTE_LABELS['opportunity-map.index']).toBe('Action Queue');
    expect(CANONICAL_ROUTE_LABELS['opportunity-map.index']).not.toContain('Opportunity');
  });

  it('"Traffic Alerts" and "Alert Subscriptions" are distinct canonical labels on distinct routes', () => {
    expect(CANONICAL_ROUTE_LABELS['alerts.index']).toBe('Traffic Alerts');
    expect(CANONICAL_ROUTE_LABELS['notification-settings.show']).toBe('Alert Subscriptions');
    expect(CANONICAL_ROUTE_LABELS['alerts.index']).not.toBe(
      CANONICAL_ROUTE_LABELS['notification-settings.show'],
    );
  });
});

describe('R6IA-003 — Alert Subscriptions route fix', () => {
  it('Settings hub "alert-subscriptions" child routes to notification-settings.show', () => {
    const hubs = getHubNavStructure(site, readiness);
    const settings = hubs.find((h) => h.key === 'settings');
    const alertSub = settings!.children.find((c) => c.key === 'alert-subscriptions');
    expect(alertSub).toBeDefined();
    expect(alertSub!.routeName).toBe('notification-settings.show');
    // Must NOT route to alerts.index (Traffic Alerts — that is a different page)
    expect(alertSub!.routeName).not.toBe('alerts.index');
  });

  it('Settings hub "alert-subscriptions" child label is "Alert Subscriptions"', () => {
    const hubs = getHubNavStructure(site, readiness);
    const settings = hubs.find((h) => h.key === 'settings');
    const alertSub = settings!.children.find((c) => c.key === 'alert-subscriptions');
    expect(alertSub!.label).toBe('Alert Subscriptions');
  });
});

describe('R6IA-002/003 — Route coverage allowlist', () => {
  /**
   * Every site-scoped nav child routeName must appear in CANONICAL_ROUTE_LABELS.
   * This is the machine-verifiable version of "every page has a canonical label."
   */
  it('all hub child routeNames are covered by CANONICAL_ROUTE_LABELS', () => {
    const hubs = getHubNavStructure(site, readiness, 'team');
    const uncovered: string[] = [];
    for (const hub of hubs) {
      for (const child of hub.children) {
        if (!(child.routeName in CANONICAL_ROUTE_LABELS)) {
          uncovered.push(`${hub.key}.${child.key} → ${child.routeName}`);
        }
      }
    }
    expect(uncovered).toEqual([]);
  });

  /**
   * Allowlist of known site-scoped /sites/{id}/* routes that are NOT sidebar
   * nav children — deep-link-only, detail pages, or hub-only surfaces. This
   * test prevents accidental nav-destination regressions without requiring
   * every route to be in the sidebar.
   */
  it('known orphan routes are documented in CANONICAL_ROUTE_LABELS', () => {
    // These were previously "orphan" surfaces — now reconciled
    expect('hour-queue.show' in CANONICAL_ROUTE_LABELS).toBe(true);
    expect('spend.index' in CANONICAL_ROUTE_LABELS).toBe(true);
    // Opportunities hub surfaces (collapsed — hub page is the entry point)
    expect('recommendations.index' in CANONICAL_ROUTE_LABELS).toBe(true);
    expect('opportunity-map.index' in CANONICAL_ROUTE_LABELS).toBe(true);
    expect('cannibalization.index' in CANONICAL_ROUTE_LABELS).toBe(true);
    expect('freshness.index' in CANONICAL_ROUTE_LABELS).toBe(true);
    expect('sites.link-opportunities.index' in CANONICAL_ROUTE_LABELS).toBe(true);
    expect('topic-clusters.index' in CANONICAL_ROUTE_LABELS).toBe(true);
    // Overview hub surfaces moved to hub page
    expect('pipeline.index' in CANONICAL_ROUTE_LABELS).toBe(true);
    expect('alerts.index' in CANONICAL_ROUTE_LABELS).toBe(true);
  });
});
