import { TOKEN } from "@/middleware";
import {
  changeUserPassword,
  createToken,
  getOneUser,
  getOneUserEmail,
  hashPassword,
  newUser,
  updateUser,
  validateCredentials,
  verifyToken,
} from "@/utils/auths";
import { sendEmail } from "@/utils/email";
import { ActionError, defineAction } from "astro:actions";
import { z } from "astro:schema";
import { SignJWT } from "jose";
import { nanoid } from "nanoid";

// The JWT secret
const secret = new TextEncoder().encode(import.meta.env.SESSION_SECRET);
export const login = defineAction({
  accept: "form",
  input: z.object({
    email: z.string().email(),
    password: z.string(),
  }),
  handler: async ({ email, password }, ctx) => {
    const hash = await hashPassword(password);
    console.log({ hash });
    // checks for email + password combinaison
    const user = await validateCredentials({ email, password });
    if (!user) {
      // if email or password incorrect, flash a message to show the user what is the error
      throw new ActionError({
        code: "UNAUTHORIZED",
        message: "Invalid email/password",
      });
    }

    // if the credentials are valid, save the user in session
    const token = await new SignJWT({
      user: { ...user, password: undefined },
    })
      .setProtectedHeader({ alg: "HS256" })
      .setJti(nanoid())
      .setIssuedAt()
      .sign(secret);

    // set JWT as a cookie
    ctx.cookies.set(TOKEN, token, {
      httpOnly: true,
      path: "/",
      maxAge: 60 * 60 * 24 * 2, // 2 days in seconds
    });

    // return a successful response
    return {
      message: "You're logged in!",
      token,
    };
  },
});
export const forgotPassword = defineAction({
  accept: "form",
  input: z.object({
    email: z.string().email(),
  }),
  handler: async ({ email }, ctx) => {
    try {
      const token = await createToken(email);
      const base_url = `${ctx.url.protocol}//${ctx.url.host}`;
      const link = `${base_url}/dashboard/reset-password?token=${token.token}`;
      console.log({ base_url, link });
      await Promise.all([
        sendEmail({
          data: {
            link,
            name: token.user?.firstName ?? "",
            email: token.user?.email ?? email,
          },
          to: email,
          subject: "PASSWORD RESET",
          template: "reset",
        }),
      ]);
      return {
        message: "Kindly check your email",
      };
    } catch (error) {
      console.log(error);
      if (error instanceof ActionError) {
        throw error;
      }
      throw new ActionError({
        code: "INTERNAL_SERVER_ERROR",
        message: "Something went wrong",
      });
    }
  },
});
export const resetPassword = defineAction({
  accept: "form",
  input: z
    .object({
      email: z.string().email(),
      token: z.string(),
      password: z.string().min(6),
      confirm_password: z.string(),
    })
    .refine((data) => data.password === data.confirm_password, {
      message: "Passwords don't match",
      path: ["confirm_password"],
    }),
  handler: async ({ email, token, password }, ctx) => {
    try {
      const valid = await verifyToken({ email, token });
      if (!valid) {
        throw new ActionError({
          code: "UNAUTHORIZED",
          message: "Invalid / Expired token",
        });
      }
      await Promise.all([changeUserPassword({ email, password })]);
      return {
        message: "Password Changed, Login to continue",
      };
    } catch (error) {
      console.log(error);
      if (error instanceof ActionError) {
        throw error;
      }
      throw new ActionError({
        code: "INTERNAL_SERVER_ERROR",
        message: "Something went wrong",
      });
    }
  },
});
export const register = defineAction({
  accept: "form",
  input: z.object({
    email: z.string().email(),
    lastName: z.string(),
    firstName: z.string(),
  }),
  handler: async ({ email, lastName, firstName }, ctx) => {
    // checks for email in db
    const exists = await getOneUserEmail(email);

    if (exists) {
      throw new ActionError({
        code: "CONFLICT",
        message: `User with Email '${email}' already exists`,
      });
    }

    // generate dummy password and hash
    const password = await hashPassword(
      firstName.trim().split(" ")[0].toLowerCase()
    );

    try {
      await newUser({
        lastName,
        firstName,
        email,
        password,
      });
    } catch (error) {
      throw new ActionError({
        code: "INTERNAL_SERVER_ERROR",
        message: "Error Creating User",
        stack: JSON.stringify(error),
      });
    }

    // return a successful response
    return {
      message: "Account Created!",
    };
  },
});
export const logout = defineAction({
  input: z.object({}).optional().nullable(),
  handler: async (input, ctx) => {
    ctx.cookies.delete(TOKEN, { path: "/" });
  },
});
export const updateProfile = defineAction({
  accept: "form",
  input: z.object({
    email: z.string().email(),
    lastName: z.string(),
    firstName: z.string(),
    phone: z.string(),
    address: z.string(),
    username: z.string(),
    id: z.number(),
    self: z.boolean().optional().nullable(),
  }),
  handler: async (
    { email, firstName, lastName, username, phone, address, id, self },
    ctx
  ) => {
    const user = await updateUser(
      {
        email,
        firstName,
        lastName,
        username,
        phone,
        address,
      },
      id
    );

    if (!user) {
      throw new ActionError({
        code: "INTERNAL_SERVER_ERROR",
        message: "Error Updating User",
      });
    }

    if (self) {
      const dbUser = await getOneUser(id);
      // @ts-ignore
      ctx.locals.user = { ...dbUser, password: undefined };
      // if the credentials are valid, save the user in session
      const token = await new SignJWT({
        user: { ...dbUser, password: undefined },
      })
        .setProtectedHeader({ alg: "HS256" })
        .setJti(nanoid())
        .setIssuedAt()
        .sign(secret);

      // set JWT as a cookie
      ctx.cookies.set(TOKEN, token, {
        httpOnly: true,
        path: "/",
        maxAge: 60 * 60 * 24 * 2, // 2 days in seconds
      });
    }

    // @ts-ignore
    console.log(ctx.locals.user);

    return {
      message: "Profile Updated Successfully",
    };
  },
});
