// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next";
import { app } from "@/firebase";
import {
  getFirestore,
  collection,
  getDocs,
  setDoc,
  doc,
} from "firebase/firestore";
import { withIronSessionApiRoute } from "iron-session/next";
import { sessionOptions } from "@/lib/session";
import axios from "axios";
import { DurationUnit, TokenError, TokenSuccess } from "@/interfaces/paypal";
import { base64 } from "@firebase/util";
import { v4 } from "uuid";
import { OrderResponseBody } from "@paypal/paypal-js";
import { getAccessToken } from "@/utils/paypal";
import moment from "moment";
import { Group } from "@/interfaces/user";

const db = getFirestore(app);

export default withIronSessionApiRoute(handler, sessionOptions);

async function handler(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== "POST")
    return res.status(404).json({ ok: false, reason: "Method not supported!" });
  if (!req.session.user) return res.status(401).json({ ok: false });

  const accessToken = await getAccessToken();
  if (!accessToken)
    return res.status(401).json({ ok: false, reason: "Authorization failed!" });

  const { id, duration, duration_unit, trackingId } = req.body as {
    id: string;
    duration: number;
    duration_unit: DurationUnit;
    trackingId: string;
  };

  if (!trackingId)
    return res.status(401).json({ ok: false, reason: "Missing tracking id!" });

  const url = `${process.env.PAYPAL_ACCESS_TOKEN_URL}/v2/checkout/orders/${id}/capture`;
  const headers = {
    headers: {
      Authorization: `Bearer ${accessToken}`,
      "PayPal-Client-Metadata-Id": trackingId,
    },
  };
  axios
    .post(url, {}, headers)
    .then(async (request) => {
      if (request.data.status === "COMPLETED") {
        const user = req.session.user;
        const subscriptionExpirationDate =
          user!.subscriptionExpirationDate;
        const today = moment(new Date());
        const dateToBeAddedTo = !subscriptionExpirationDate
          ? today
          : moment(subscriptionExpirationDate).isAfter(today)
          ? moment(subscriptionExpirationDate)
          : today;

        const updatedExpirationDate = dateToBeAddedTo.add(
          duration,
          duration_unit
        );
        await setDoc(
          doc(db, "users", req.session.user!.id),
          {
            subscriptionExpirationDate: updatedExpirationDate.toISOString(),
            status: "active",
          },
          { merge: true }
        );

        try {
          await setDoc(doc(db, "paypalpayments", v4()), {
            orderId: id,
            userId: req.session.user!.id,
            status: request.data.status,
            createdAt: new Date().toISOString(),
            value:
              request.data.purchase_units[0].payments.captures[0].amount.value,
            currency:
              request.data.purchase_units[0].payments.captures[0].amount
                .currency_code,
            subscriptionDuration: duration,
            subscriptionDurationUnit: duration_unit,
            subscriptionExpirationDate: updatedExpirationDate.toISOString(),
          });
        } catch (err) {
          console.error("Failed to insert paypal payment!", err);
        }

        if (user!.type === "corporate") {
          const snapshot = await getDocs(collection(db, "groups"));
          const groups: Group[] = (
            snapshot.docs.map((doc) => ({
              id: doc.id,
              ...doc.data(),
            })) as Group[]
          ).filter((x) => x.admin === user!.id);

          await Promise.all(
            groups
              .flatMap((x) => x.participants)
              .map(
                async (x) =>
                  await setDoc(
                    doc(db, "users", x),
                    {
                      subscriptionExpirationDate:
                        updatedExpirationDate.toISOString(),
                      status: "active",
                    },
                    { merge: true }
                  )
              )
          );
        }

        return res.status(200).json({ ok: true });
      }

      res.status(404).json({
        ok: false,
        reason: "Order ID not found or purchase was not approved!",
      });
    })
    .catch((err) => {
      console.error(err.response.status, err.response.data);
      res.status(err.response.status).json(err.response.data);
    });
}
