import type { MiddlewareHandler } from 'hono';
import { z } from 'zod';

import { logger } from '@/shared/logger';

const claimsSchema = z.object({
  sub: z.string(),
  email: z.string().optional(),
  admin: z.boolean().optional(),
});

export type UserClaims = z.infer<typeof claimsSchema>;

declare module 'hono' {
  interface ContextVariableMap {
    user: UserClaims;
  }
}

const BEARER_RE = /^Bearer\s+(.+)$/i;

export function ssoRequired(ssoValidateUrl: string, serviceToken: string): MiddlewareHandler {
  return async (c, next) => {
    const authHeader = c.req.header('authorization') ?? '';
    const bearerMatch = BEARER_RE.exec(authHeader);

    if (bearerMatch) {
      if (bearerMatch[1] === serviceToken) {
        c.set('user', { sub: 'service', admin: false });
        await next();
        return;
      }
      return c.json({ error: 'authentication_required', code: 'unauthenticated' }, 401);
    }

    const cookie = c.req.header('cookie') ?? '';
    if (!cookie) {
      return c.json({ error: 'authentication_required', code: 'unauthenticated' }, 401);
    }

    try {
      const res = await fetch(ssoValidateUrl, {
        method: 'GET',
        headers: {
          Cookie: cookie,
          'X-Original-URI': new URL(c.req.url).pathname,
          'X-Forwarded-Host': c.req.header('host') ?? '',
        },
      });

      if (res.status !== 200) {
        return c.json({ error: 'authentication_required', code: 'unauthenticated' }, 401);
      }

      const body = await res.json().catch(() => null);
      const parsed = claimsSchema.safeParse(body);
      if (!parsed.success) {
        logger.error('SSO validate returned malformed claims', { body });
        return c.json({ error: 'authentication_required', code: 'unauthenticated' }, 401);
      }
      c.set('user', parsed.data);
    } catch (err) {
      logger.error('SSO validate request failed', { error: String(err) });
      return c.json({ error: 'auth_service_unavailable', code: 'sso_error' }, 503);
    }

    await next();
  };
}
