import type { FastifyInstance, FastifyReply, FastifyRequest } from "fastify"
import fp from "fastify-plugin"
import { env } from "../config.js"
import {
  hasPermission,
  permissionForRequest,
  type AuthUser,
} from "../lib/permissions.js"

declare module "fastify" {
  interface FastifyRequest {
    authUser?: AuthUser
  }
}

declare module "@fastify/jwt" {
  interface FastifyJWT {
    payload: {
      sub: string
      email?: string
      name?: string
      apps?: string[]
      permissions?: string[]
      is_admin?: boolean
      iss?: string
      exp?: number
    }
    user: {
      sub: string
      email?: string
      name?: string
      apps?: string[]
      permissions?: string[]
      is_admin?: boolean
      iss?: string
      exp?: number
    }
  }
}

async function authPlugin(app: FastifyInstance) {
  if (env.authRequired && env.jwtSecret.length < 8) {
    throw new Error("AUTH_JWT_SECRET / JWT_SECRET required when AUTH_REQUIRED=true")
  }

  await app.register(import("@fastify/jwt"), {
    secret: env.jwtSecret,
    ...(env.authRequired
      ? {
          verify: {
            allowedIss: [env.authIssuer],
          },
        }
      : {}),
  })

  if (env.authRequired) {
    app.log.info(
      { issuer: env.authIssuer, portal: env.authPortalUrl },
      "AUTH_REQUIRED=true — portal JWT middleware enabled",
    )
  } else {
    app.log.info("AUTH_REQUIRED=false — /api/* open without JWT")
  }
}

/**
 * Protect /api/* when AUTH_REQUIRED=true.
 * Public: /health, /api/auth/config
 */
export async function requireAuth(
  request: FastifyRequest,
  reply: FastifyReply,
): Promise<void> {
  if (!env.authRequired) return

  const pathname = (request.url.split("?")[0] ?? request.url)
  if (pathname === "/api/auth/config") return

  const authHeader = request.headers.authorization ?? ""
  const token = authHeader.startsWith("Bearer ") ? authHeader.slice(7) : ""
  if (!token) {
    return reply.code(401).send({ error: "Unauthorized" })
  }

  try {
    await request.jwtVerify()
  } catch {
    return reply.code(401).send({ error: "Unauthorized" })
  }

  const payload = request.user
  const apps = Array.isArray(payload.apps) ? payload.apps.map(String) : []
  const permissions = Array.isArray(payload.permissions)
    ? payload.permissions.map(String)
    : []

  if (!apps.includes("mm")) {
    return reply
      .code(403)
      .send({ error: "Нет доступа к приложению MikrotikManager" })
  }

  request.authUser = {
    id: String(payload.sub),
    email: String(payload.email ?? ""),
    name: String(payload.name ?? ""),
    apps,
    permissions,
    isAdmin: Boolean(payload.is_admin),
  }

  const required = permissionForRequest(request.method, pathname)
  if (required && !hasPermission(permissions, required)) {
    return reply.code(403).send({ error: `Недостаточно прав: ${required}` })
  }
}

export default fp(authPlugin, { name: "auth" })
