/**
 * Local-auth HTTP routes + helpers: login, self-signup, login-page rendering,
 * and the primary=local provider gating.
 *
 * See docs/superpowers/plans/2026-06-09-local-auth.md.
 */
import { afterEach, beforeEach, describe, it, expect } from 'vitest';
import { mkdtempSync, rmSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
import express, { type Express } from 'express';
import request from 'supertest';
import { Repository } from '../db/repository.js';
import { runMigrations } from '../db/migrate.js';
import { setupAuth, isLocalEnabled, isProviderActive } from './auth.js';
import type { AuthConfig } from '../config.js';

function mkAuthConfig(over: Partial<AuthConfig> = {}): AuthConfig {
  return {
    sessionSecret: 'test-secret',
    sessionMaxAge: 3600_000,
    secureCookie: false,
    adminEmails: [],
    providers: {},
    local: { enabled: true, allowSignup: true },
    ...over,
  };
}

function mkApp(repo: Repository, authConfig: AuthConfig): Express {
  const app = express();
  const auth = setupAuth(repo, authConfig);
  app.use(auth.sessionMiddleware);
  app.use(auth.passportInit);
  app.use(auth.passportSession);
  app.use('/auth', auth.authRouter);
  app.get('/', (_req, res) => res.send('home'));
  return app;
}

describe('isLocalEnabled', () => {
  it('reflects auth.local.enabled', () => {
    expect(isLocalEnabled(mkAuthConfig({ local: { enabled: true } }))).toBe(true);
    expect(isLocalEnabled(mkAuthConfig({ local: { enabled: false } }))).toBe(false);
    expect(isLocalEnabled(mkAuthConfig({ local: undefined }))).toBe(false);
  });
});

describe('isProviderActive with primary=local', () => {
  it('turns OAuth providers OFF when primary is local', () => {
    const c = mkAuthConfig({
      primaryProvider: 'local',
      providers: {
        google: { clientId: 'g', clientSecret: 's', callbackUrl: 'http://x/cb' },
      },
    });
    expect(isProviderActive(c, 'google')).toBe(false);
  });
});

describe('local auth routes', () => {
  let tempDir = '';
  let repo: Repository;
  let app: Express;

  beforeEach(() => {
    tempDir = mkdtempSync(join(tmpdir(), 'maestro-localroutes-'));
    repo = new Repository(join(tempDir, 'orchestrator.db'));
    runMigrations(repo.getDb());
    app = mkApp(repo, mkAuthConfig());
  });
  afterEach(() => {
    repo.close();
    if (tempDir) { rmSync(tempDir, { recursive: true, force: true }); tempDir = ''; }
  });

  it('GET /auth/login renders the local form when enabled', async () => {
    const res = await request(app).get('/auth/login');
    expect(res.status).toBe(200);
    expect(res.text).toContain('action="/auth/local"');
    expect(res.text).toContain('action="/auth/local/signup"'); // signup on
  });

  it('POST /auth/local with valid active creds redirects home', async () => {
    repo.createLocalUser({ email: 'a@x.com', password: 'pw12345678', role: 'user', status: 'active' });
    const res = await request(app).post('/auth/local').type('form').send({ email: 'a@x.com', password: 'pw12345678' });
    expect(res.status).toBe(302);
    expect(res.headers['location']).toBe('/');
  });

  it('POST /auth/local with wrong password redirects to login error', async () => {
    repo.createLocalUser({ email: 'b@x.com', password: 'rightpassword', role: 'user', status: 'active' });
    const res = await request(app).post('/auth/local').type('form').send({ email: 'b@x.com', password: 'nope' });
    expect(res.status).toBe(302);
    expect(res.headers['location']).toContain('/auth/login?error=');
  });

  it('POST /auth/local for a pending user redirects to the pending page', async () => {
    repo.createLocalUser({ email: 'c@x.com', password: 'pw12345678', role: 'user', status: 'pending' });
    const res = await request(app).post('/auth/local').type('form').send({ email: 'c@x.com', password: 'pw12345678' });
    expect(res.status).toBe(302);
    expect(res.headers['location']).toBe('/auth/pending');
  });

  it('POST /auth/local/signup creates a pending user', async () => {
    const res = await request(app).post('/auth/local/signup').type('form').send({ email: 'new@x.com', password: 'pw12345678' });
    expect(res.status).toBe(302);
    expect(res.headers['location']).toBe('/auth/pending');
    const u = repo.getUserByEmail('new@x.com');
    expect(u?.status).toBe('pending');
    expect(repo.verifyLocalPassword(u!.id, 'pw12345678')).toBe(true);
  });

  it('POST /auth/local/signup rejects a short password', async () => {
    const res = await request(app).post('/auth/local/signup').type('form').send({ email: 'short@x.com', password: 'abc' });
    expect(res.status).toBe(302);
    expect(res.headers['location']).toContain('error=weak');
    expect(repo.getUserByEmail('short@x.com')).toBeNull();
  });

  it('POST /auth/local/signup does not disclose an existing email (generic error, no overwrite)', async () => {
    repo.createLocalUser({ email: 'dup@x.com', password: 'originalpw', role: 'user', status: 'active' });
    const res = await request(app).post('/auth/local/signup').type('form').send({ email: 'dup@x.com', password: 'attackerpw' });
    expect(res.status).toBe(302);
    expect(res.headers['location']).toContain('error=signup');
    // original password unchanged (no takeover)
    const u = repo.getUserByEmail('dup@x.com')!;
    expect(repo.verifyLocalPassword(u.id, 'originalpw')).toBe(true);
    expect(repo.verifyLocalPassword(u.id, 'attackerpw')).toBe(false);
  });

  it('signup route is absent when allowSignup is off', async () => {
    const app2 = mkApp(repo, mkAuthConfig({ local: { enabled: true, allowSignup: false } }));
    const res = await request(app2).post('/auth/local/signup').type('form').send({ email: 'x@x.com', password: 'pw12345678' });
    expect(res.status).toBe(404);
  });
});
