import type { FreshContext, Plugin } from "$fresh/server.ts";
import { assert } from "$std/assert/assert.ts";
import { createServerClient, parse, serialize } from "@supabase/ssr";
import { Session } from "@supabase/supabase-js";
import { redirect } from "../utils.ts";

export type SignedInState = {
  session: Session;
};

export type SignedOutState = {
  session?: null;
};

export type AuthState = SignedInState | SignedOutState;

export const authPlugin: Plugin = {
  name: "auth",
  middlewares: [
    // For every route, we ensure the session state is updated
    {
      path: "/",
      middleware: {
        handler: setSessionState,
      },
    },

    // For the dashboard route, we ensure the user is signed in
    {
      path: "/dashboard",
      middleware: {
        handler: ensureSignedIn,
      },
    },

    // For the dashboard route, we ensure the user is signed in
    {
      path: "/binary",
      middleware: {
        handler: ensureSignedIn,
      },
    },

    // For the dashboard route, we ensure the user is signed in
    {
      path: "/audio",
      middleware: {
        handler: ensureSignedIn,
      },
    },

    // For admin route, we unsure the user is signed in and is an admin
    {
      path: "/admin",
      middleware: {
        handler: ensureSignedInAndAdmin,
      },
    },
  ],
};

async function setSessionState(req: Request, ctx: FreshContext) {
  if (ctx.destination !== "route") return await ctx.next();

  // Sanity check - start without a session
  ctx.state.session = null;

  // Create an empty response object here. We want to make sure we do this
  // session refresh before going further down the middleware chain
  const resp = new Response();
  const supabase = createSupabaseClient(req, resp);

  // Refresh session if expired
  const { data } = await supabase.auth.getSession();

  ctx.state.session = data.session;

  // Continue down the middleware chain
  const nextResp = await ctx.next();

  // Copy over any headers that were added by Supabase
  for (const [key, value] of resp.headers) {
    nextResp.headers.set(key, value);
  }

  return nextResp;
}

function ensureSignedInAndAdmin(_req: Request, ctx: FreshContext) {
  if (!ctx.state.session) {
    return redirect(
      "/auth/signin?message=You must be signed in to access this page",
    );
  }

  if ((ctx.state.session as { user: { email: string } }).user.email != 'YOUR_ADMIN_EMAIL') {
    return redirect(
      "/dashboard",
    );
  }

  return ctx.next();
}

function ensureSignedIn(_req: Request, ctx: FreshContext) {
  if (!ctx.state.session) {
    return redirect(
      "/auth/signin?message=You must be signed in to access this page",
    );
  }

  return ctx.next();
}

export function createSupabaseClient(req: Request, resp: Response) {
  const cookies = parse(req.headers.get("Cookie") || "");

  const SUPABASE_URL = Deno.env.get("SUPABASE_URL");
  const ANON_KEY = Deno.env.get("SUPABASE_ANON_KEY");

  assert(SUPABASE_URL, "SUPABASE_URL is not set");
  assert(ANON_KEY, "SUPABASE_ANON_KEY is not set");

  const supabase = createServerClient(
    SUPABASE_URL,
    ANON_KEY,
    {
      cookies: {
        get(key) {
          return cookies[key];
        },
        set(key, value, options) {
          const cookie = serialize(key, value, options);
          // If the cookie is updated, update the cookies for the response
          resp.headers.append("Set-Cookie", cookie);
        },
        remove(key, options) {
          const cookie = serialize(key, "", options);
          // If the cookie is removed, update the cookies for the response
          resp.headers.append("Set-Cookie", cookie);
        },
      },
    },
  );

  return supabase;
}