import { redirect } from "next/navigation"
import { auth } from "@/lib/auth"
import { prisma } from "@/lib/prisma"
import { Beer, ShieldCheck } from "lucide-react"
import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { ConsentForm } from "@/components/oauth/consent-form"
import {
  buildRedirect,
  validateAuthorizeRequest,
} from "@/lib/mcp/authorize-request"
import {
  AUTH_CODE_TTL_MS,
  SCOPE_DESCRIPTIONS,
  coversScopes,
  generateAuthCode,
} from "@/lib/mcp/oauth"

/**
 * The consent screen.
 *
 * Not in PUBLIC_ROUTES, so the middleware bounces an unauthenticated visitor to
 * /login for us - and now that login honours ?callbackUrl they come straight
 * back here afterwards. The explicit session check below is for the callbackUrl
 * construction, not for access control.
 *
 * Deliberately outside the (app) route group: this is a credential prompt, and
 * it should not be wrapped in the sidebar and bottom nav of the signed-in app.
 */
export const dynamic = "force-dynamic"

type SearchParams = Record<string, string | string[] | undefined>

function toParams(searchParams: SearchParams): URLSearchParams {
  const params = new URLSearchParams()
  for (const [key, value] of Object.entries(searchParams)) {
    if (typeof value === "string") params.set(key, value)
    else if (Array.isArray(value) && value[0]) params.set(key, value[0])
  }
  return params
}

export default async function AuthorizePage({
  searchParams,
}: {
  searchParams: SearchParams
}) {
  const search = toParams(searchParams)
  const validation = await validateAuthorizeRequest(search)

  if (validation.kind === "fatal") {
    return <ErrorCard title="This link isn't valid" detail={validation.message} />
  }

  if (validation.kind === "redirectable") {
    redirect(
      buildRedirect(validation.redirectUri, {
        error: validation.error,
        error_description: validation.description,
        state: validation.state,
      })
    )
  }

  const { params, client } = validation

  const session = await auth()
  if (!session?.user?.id) {
    redirect(`/login?callbackUrl=${encodeURIComponent(`/oauth/authorize?${search}`)}`)
  }

  // A session already implies an ACTIVE user - the jwt callback in lib/auth.ts
  // returns null for anyone suspended or deleted, which clears the cookie.
  const userId = session.user.id

  // Already approved for these scopes? Skip the screen. This is what makes a
  // re-connection after an expired refresh token silent rather than another
  // round of clicking Allow.
  const existing = await prisma.oAuthGrant.findUnique({
    where: { clientId_userId: { clientId: client.id, userId } },
    select: { id: true, scopes: true, revokedAt: true },
  })

  if (existing && !existing.revokedAt && coversScopes(existing.scopes, params.scopes)) {
    const code = await createAuthCode(client.id, userId, params)
    redirect(buildRedirect(params.redirectUri, { code, state: params.state }))
  }

  return (
    <div className="min-h-screen flex items-center justify-center bg-background p-4">
      <Card className="w-full max-w-md">
        <CardHeader className="text-center">
          <div className="flex justify-center mb-4">
            <Beer className="h-10 w-10 text-primary" />
          </div>
          <CardTitle className="text-xl">
            Connect {client.clientName} to DrinkTracker?
          </CardTitle>
          <CardDescription>
            Signed in as {session.user.email ?? session.user.name}
          </CardDescription>
        </CardHeader>
        <CardContent className="space-y-5">
          <div className="space-y-2">
            <p className="text-sm font-medium">It will be able to:</p>
            <ul className="space-y-1.5">
              {params.scopes.map((scope) => (
                <li key={scope} className="flex gap-2 text-sm text-muted-foreground">
                  <ShieldCheck className="mt-0.5 h-4 w-4 shrink-0 text-primary" />
                  <span>{SCOPE_DESCRIPTIONS[scope] ?? scope}</span>
                </li>
              ))}
            </ul>
          </div>

          {client.clientUri && (
            <p className="text-xs text-muted-foreground break-all">
              {client.clientUri}
            </p>
          )}

          <p className="text-xs text-muted-foreground">
            It will be sent back to <code className="break-all">{params.redirectUri}</code>.
            You can disconnect it any time from Settings.
          </p>

          <ConsentForm search={search.toString()} />
        </CardContent>
      </Card>
    </div>
  )
}

async function createAuthCode(
  clientRowId: string,
  userId: string,
  params: {
    redirectUri: string
    scopes: string[]
    codeChallenge: string
    resource: string | null
  }
): Promise<string> {
  const { code, hash } = generateAuthCode()
  await prisma.oAuthAuthCode.create({
    data: {
      codeHash: hash,
      clientId: clientRowId,
      userId,
      redirectUri: params.redirectUri,
      resource: params.resource,
      scopes: params.scopes,
      codeChallenge: params.codeChallenge,
      codeChallengeMethod: "S256",
      expiresAt: new Date(Date.now() + AUTH_CODE_TTL_MS),
    },
  })
  return code
}

function ErrorCard({ title, detail }: { title: string; detail: string }) {
  return (
    <div className="min-h-screen flex items-center justify-center bg-background p-4">
      <Card className="w-full max-w-md">
        <CardHeader className="text-center">
          <CardTitle className="text-xl">{title}</CardTitle>
          <CardDescription>{detail}</CardDescription>
        </CardHeader>
        <CardContent>
          <p className="text-center text-sm text-muted-foreground">
            Nothing was connected. Try starting the connection again from the app
            you were using.
          </p>
        </CardContent>
      </Card>
    </div>
  )
}
