import { createIdentity } from "actions/createIdentity";
import { subscribeToPublication } from "app/lish/subscribeToPublication";
import { drizzle } from "drizzle-orm/node-postgres";
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 { pool } from "supabase/pool";

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;
      // 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:generic transition:email",
        signal: ac.signal,
        state: JSON.stringify(state),
      });

      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 client = await pool.connect();
          const db = drizzle(client);
          identity = await createIdentity(db, { atp_did: session.did });
          client.release();
        }
        let { data: token } = await supabaseServerClient
          .from("email_auth_tokens")
          .insert({
            identity: identity.id,
            confirmed: true,
            confirmation_code: "",
          })
          .select()
          .single();

        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) {
        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.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);
};
