import { subscribeToPublication } from "app/lish/subscribeToPublication";
import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { NextRequest, NextResponse } from "next/server";
import { createOauthClient } from "src/atproto-oauth";
import { setAuthToken } from "src/auth";

import { supabaseServerClient } from "supabase/serverClient";
import { URLSearchParams } from "url";
import {
  ActionAfterSignIn,
  parseActionFromSearchParam,
} from "./afterSignInActions";
import { inngest } from "app/api/inngest/client";

type OauthRequestClientState = {
  redirect: string | null;
  action: ActionAfterSignIn | null;
};

export async function GET(
  req: NextRequest,
  props: { params: Promise<{ route: string; handle?: string }> },
) {
  const params = await props.params;
  let client = await createOauthClient();
  switch (params.route) {
    case "metadata":
      return NextResponse.json(client.clientMetadata);
    case "jwks":
      return NextResponse.json(client.jwks);
    case "login": {
      const searchParams = req.nextUrl.searchParams;
      const handle = searchParams.get("handle") as string;
      const signup = searchParams.get("signup") === "true";
      // Put originating page here!
      let redirect = searchParams.get("redirect_url");
      if (redirect) redirect = decodeURIComponent(redirect);
      let action = parseActionFromSearchParam(searchParams.get("action"));
      let state: OauthRequestClientState = { redirect, action };

      // Revoke any pending authentication requests if the connection is closed (optional)
      const ac = new AbortController();

      const url = await client.authorize(handle || "https://bsky.social", {
        scope:
          "atproto transition:email include:pub.leaflet.authFullPermissions include:site.standard.authFull include:app.bsky.authCreatePosts include:app.bsky.authViewAll?aud=did:web:api.bsky.app%23bsky_appview rpc:parts.page.mention.search?aud=* blob:*/*",
        signal: ac.signal,
        state: JSON.stringify(state),
        ...(signup ? { prompt: "create" } : {}),
      });

      return NextResponse.redirect(url);
    }
    case "callback": {
      const params = new URLSearchParams(req.url.split("?")[1]);

      let redirectPath = "/";
      try {
        const { session, state } = await client.callback(params);
        let s: OauthRequestClientState = JSON.parse(state || "{}");
        redirectPath = decodeURIComponent(s.redirect || "/");
        let { data: identity } = await supabaseServerClient
          .from("identities")
          .select()
          .eq("atp_did", session.did)
          .single();
        if (!identity) {
          let existingIdentity = (await cookies()).get("auth_token");
          if (existingIdentity) {
            let data = await supabaseServerClient
              .from("email_auth_tokens")
              .select("*, identities(*)")
              .eq("id", existingIdentity.value)
              .single();
            if (data.data?.identity && data.data.confirmed)
              await supabaseServerClient
                .from("identities")
                .update({ atp_did: session.did })
                .eq("id", data.data.identity);

            return handleAction(s.action, redirectPath);
          }
          const { data } = await supabaseServerClient
            .from("identities")
            .insert({ atp_did: session.did })
            .select()
            .single();
          identity = data;
        }

        // Trigger migration if identity needs it
        const metadata = identity?.metadata as Record<string, unknown> | null;
        if (metadata?.needsStandardSiteMigration) {
          if (process.env.NODE_ENV === "production")
            await inngest.send({
              name: "user/migrate-to-standard",
              data: { did: session.did },
            });
        }

        let { data: token } = await supabaseServerClient
          .from("email_auth_tokens")
          .insert({
            identity: identity!.id,
            confirmed: true,
            confirmation_code: "",
          })
          .select()
          .single();
        console.log({ token });
        if (token) await setAuthToken(token.id);

        // Process successful authentication here
        console.log("authorize() was called with state:", state);

        console.log("User authenticated as:", session.did);
        return handleAction(s.action, redirectPath);
      } catch (e) {
        console.log(e);
        redirect(redirectPath);
      }
    }
    default:
      return NextResponse.json({ error: "Invalid route" }, { status: 404 });
  }
}

const handleAction = async (
  action: ActionAfterSignIn | null,
  redirectPath: string,
) => {
  let parsePath = decodeURIComponent(redirectPath);
  let url;
  if (parsePath.includes("://")) url = new URL(parsePath);
  else url = new URL(decodeURIComponent(redirectPath), "https://example.com");
  if (action?.action === "subscribe") {
    let result = await subscribeToPublication(action.publication);
    if (result.success && result.hasFeed === false)
      url.searchParams.set("showSubscribeSuccess", "true");
  }

  let path = url.pathname;
  if (url.search) path += url.search;
  if (url.hash) path += url.hash;
  return parsePath.includes("://") ? redirect(url.toString()) : redirect(path);
};
