import { describe, it, expect } from 'vitest';
import {
  generate,
  buildLoginComponent,
  buildWiringFile,
  injectLoginExtensions,
  LOGIN_IMPORT_LINE,
} from '../generate.js';
import { ScaffoldLoginPageInputSchema, type ScaffoldLoginPageInput } from '../types.js';

/** Parse through the schema so defaults (componentName, hrefs, …) apply. */
function spec(overrides: Record<string, unknown> = {}): ScaffoldLoginPageInput {
  return ScaffoldLoginPageInputSchema.parse({
    projectPath: '/proj',
    appCode: 'demo',
    providers: { local: true },
    ...overrides,
  });
}

const LAYOUT = { webDir: 'web/demo-web' };

const CANONICAL_MAIN = `import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import { BrowserRouter } from 'react-router-dom';
import { SmartStackProvider } from '@atlashub/smartstack';
import App from './App';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <BrowserRouter>
      <SmartStackProvider
        config={{
          apiUrl: import.meta.env.VITE_API_URL || 'http://localhost:5142',
          extensions: {}
        }}
      >
        <App />
      </SmartStackProvider>
    </BrowserRouter>
  </StrictMode>,
);
`;

// main.tsx already wired for /site-vitrine — login wiring must coexist.
const MAIN_WITH_VITRINE = `import { vitrineExtensions } from './extensions/vitrine.generated';
import { SmartStackProvider } from '@atlashub/smartstack';

createRoot(document.getElementById('root')!).render(
  <SmartStackProvider
    config={{
      apiUrl: 'x',
      extensions: { ...vitrineExtensions }
    }}
  >
    <App />
  </SmartStackProvider>,
);
`;

describe('buildLoginComponent — local form', () => {
  it('imports + invokes useLoginForm and renders the email/password form', () => {
    const src = buildLoginComponent(spec());
    expect(src).toContain(`import { useLoginForm } from '@atlashub/smartstack'`);
    expect(src).toContain('useLoginForm()');
    expect(src).toContain('onSubmit={handleSubmit}');
    expect(src).toContain('Se connecter');
    expect(src).toContain('PageProps');
  });

  it('shows the forgot-password link by default, honors the href, and can hide it', () => {
    expect(buildLoginComponent(spec())).toContain('/forgot-password');
    const hidden = buildLoginComponent(spec({ showForgotPassword: false }));
    expect(hidden).not.toContain('Mot de passe oublié');
  });
});

describe('buildLoginComponent — external providers', () => {
  it('emits only the requested provider buttons with correct targets', () => {
    const src = buildLoginComponent(spec({ providers: { local: true, microsoft: true, google: true } }));
    expect(src).toContain("globalThis.location.href = \"/api/auth/microsoft\"");
    expect(src).toContain("globalThis.location.href = \"/api/auth/google\"");
    expect(src).not.toContain('/sso');
  });

  it('entra button targets /sso', () => {
    const src = buildLoginComponent(spec({ providers: { local: true, entra: true } }));
    expect(src).toContain("globalThis.location.href = \"/sso\"");
  });

  it('SSO-only (no local) omits useLoginForm entirely', () => {
    const src = buildLoginComponent(spec({ providers: { local: false, entra: true } }));
    expect(src).not.toContain('useLoginForm');
    expect(src).not.toContain('handleSubmit');
    expect(src).toContain("globalThis.location.href = \"/sso\"");
  });
});

describe('buildLoginComponent — registration + branding', () => {
  it('emits the create-account link only when allowRegistration is true', () => {
    expect(buildLoginComponent(spec({ allowRegistration: true }))).toContain('Créer un compte');
    expect(buildLoginComponent(spec({ allowRegistration: false }))).not.toContain('Créer un compte');
  });

  it('honors a custom registerHref', () => {
    const src = buildLoginComponent(spec({ allowRegistration: true, registerHref: '/signup' }));
    expect(src).toContain('"/signup"');
  });

  it('renders branding (title, subtitle, logo) as safe JSX expressions', () => {
    const src = buildLoginComponent(spec({ branding: { title: 'Acme', subtitle: 'Bienvenue', logoUrl: 'https://x/logo.png' } }));
    expect(src).toContain('{"Acme"}');
    expect(src).toContain('{"Bienvenue"}');
    expect(src).toContain('src={"https://x/logo.png"}');
  });

  it('uses a custom componentName for the exported function', () => {
    const src = buildLoginComponent(spec({ componentName: 'BrandLogin' }));
    expect(src).toContain('export function BrandLogin(');
  });
});

describe('buildWiringFile', () => {
  it('registers the component under PAGE_KEYS.LOGIN and imports it', () => {
    const w = buildWiringFile(spec({ componentName: 'BrandLogin' }));
    expect(w).toContain("import { PAGE_KEYS } from '@atlashub/smartstack'");
    expect(w).toContain("import { BrandLogin } from '../auth/BrandLogin'");
    expect(w).toContain('export const loginExtensions: ExtensionConfig = { pages: { [PAGE_KEYS.LOGIN]: BrandLogin } }');
  });
});

describe('injectLoginExtensions — main.tsx wiring', () => {
  it('wires an empty extensions block and adds the import', () => {
    const { content, status } = injectLoginExtensions(CANONICAL_MAIN);
    expect(status).toBe('applied');
    expect(content).toContain(LOGIN_IMPORT_LINE);
    expect(content).toContain('extensions: { ...loginExtensions }');
  });

  it('is idempotent — re-running leaves it unchanged', () => {
    const once = injectLoginExtensions(CANONICAL_MAIN).content;
    const twice = injectLoginExtensions(once);
    expect(twice.status).toBe('unchanged');
    expect(twice.content).toBe(once);
    expect((once.match(/\.\.\.loginExtensions/g) ?? []).length).toBe(1);
  });

  it('coexists with an existing vitrine spread', () => {
    const { content, status } = injectLoginExtensions(MAIN_WITH_VITRINE);
    expect(status).toBe('applied');
    expect(content).toContain('...loginExtensions');
    expect(content).toContain('...vitrineExtensions');
    expect(content).toMatch(/extensions:\s*\{\s*\.\.\.loginExtensions,\s*\.\.\.vitrineExtensions\s*\}/);
  });

  it('respects the @customised marker', () => {
    const src = '// @customised\n' + CANONICAL_MAIN;
    const { content, status } = injectLoginExtensions(src);
    expect(status).toBe('skipped-customised');
    expect(content).toBe(src);
  });

  it('bails to not-found (no dangling import) when there is no extensions block', () => {
    const src = 'export const x = 1;\n';
    const { content, status } = injectLoginExtensions(src);
    expect(status).toBe('not-found');
    expect(content).toBe(src);
  });
});

describe('generate — emitted files', () => {
  it('emits the page (skip-if-exists) and the wiring (overwrite) at the right paths', () => {
    const files = generate(spec({ componentName: 'CustomLoginPage' }), LAYOUT);
    expect(files.map((f) => `${f.path} [${f.strategy}]`)).toEqual([
      'web/demo-web/src/auth/CustomLoginPage.tsx [skip-if-exists]',
      'web/demo-web/src/extensions/login.generated.ts [overwrite]',
    ]);
  });
});

describe('schema refinement', () => {
  it('rejects a spec with no provider shown', () => {
    const res = ScaffoldLoginPageInputSchema.safeParse({
      projectPath: '/p',
      appCode: 'a',
      providers: { local: false },
    });
    expect(res.success).toBe(false);
  });

  it('rejects a non-PascalCase componentName', () => {
    const res = ScaffoldLoginPageInputSchema.safeParse({
      projectPath: '/p',
      appCode: 'a',
      providers: { local: true },
      componentName: 'custom-login',
    });
    expect(res.success).toBe(false);
  });
});
