#!/usr/bin/python3
import sys
from decimal import ROUND_CEILING, Decimal

sys.path.insert(0, "/usr/local/mgr5/lib/python")
import billmgr.logger as logging
import billmgr.payment as payment
import paypalexpanded.api as paypal_api
from billmgr.modules.paymentcgi import PageType, PaymentCgiType, run_cgi

logging.init_logging("ppexpandedpayment")
logger = logging.get_logger("ppexpandedpayment")

MAIN_CGI = "/mancgi/ppexpandedpayment"
RESULT_CGI = "/mancgi/ppexpandedredirect"


class PayPalPaymentCgi(paypal_api.PayPalBaseCgi):
    def cgi_type(self) -> PaymentCgiType:
        return PaymentCgiType.Payment

    def process(self):
        logger.debug(f"Processing payment: {self.payment_params}")
        auth = self.authenticate()

        existing_external_id = self.payment_params.get("externalid", "")
        if existing_external_id and self._reuse_existing_order(auth, existing_external_id):
            return

        amount = Decimal(self.payment_params["paymethodamount"]).quantize(
            paypal_api.MIN_AMOUNT, ROUND_CEILING
        )
        need_save = False
        if "store_payment" in self.payment_params and self.payment_params["store_payment"] == "on":
            logger.info("need_save = True")
            need_save = True
        funding = self.get_funding_source()
        response = paypal_api.request_create_order(
            auth=auth,
            order=paypal_api.Order(
                amount=amount,
                currency_iso=self.get_currency_iso(),
                reference_id=self.elid(),
                need_save=need_save,
                urls=paypal_api.Urls(
                    return_url=self.get_page(PageType.Success),
                    cancel_url=self.get_page(PageType.Fail),
                ),
            ),
            paymethod=self.payment_params["payment_method"],
        )
        order_id = response.json()["id"]
        payment.set_in_pay(int(self.elid()), order_id, response.text)
        logger.info(f"Created PayPal order: {order_id}")
        self.render_payment_form(response.text, amount, funding, MAIN_CGI, RESULT_CGI)

    def _reuse_existing_order(self, auth, external_id) -> bool:
        """Look at what is already stored in `externalId` before creating a new order.
        - An old payment has already been captured on the PayPal 
            side (`order_id/capture_id` in status `COMPLETED`) → do not create a new order, 
            otherwise we would overwrite the `externalId` of an already paid payment; 
            immediately send the client to success.
        - The old order is alive but not yet captured (`APPROVED`) → capture it right now, 
            instead of creating another order alongside it.
        - The old order is not yet confirmed (`CREATED`) or is dead (`DECLINED`/`FAILED`)
            → we lose nothing, so we can safely create a new order as before.

        As in `ppexpandedredirect.py`, here we only bring `externalId`
        up to date and decide where to redirect the client; 
        the final `payment.set_paid` (taking into account `store_payment`/token) 
        still does `checkpay` or the webhook.

        Returns `True` if a response has already been sent
        to the client and there is no need to create a new order.
        """
        try:
            if "/" in external_id:
                _, capture_id = external_id.split("/", 1)
                status = paypal_api.request_show_captured_payment_details(auth, capture_id).json()[
                    "status"
                ]
                if status != paypal_api.CaptureStatus.COMPLETED.name:
                    return False
                logger.info(
                    f"payment {self.elid()} already captured ({external_id}), skipping new order"
                )
                self.redirect_to_url(self.get_page(PageType.Success))
                return True

            order_id = external_id
            data = paypal_api.request_show_order_details(auth, order_id).json()
            status = data["status"]

            if status == paypal_api.CaptureStatus.APPROVED.name:
                capture_resp = paypal_api.request_capture_order(auth, order_id)
                captured = paypal_api.parse_capture_order(capture_resp)
                new_external_id = f"{captured.order_id}/{captured.capture_id}"
                payment.set_in_pay(int(self.elid()), new_external_id)
                logger.info(
                    f"captured previously approved order {order_id} instead of creating a new one"
                )
                self.redirect_to_url(
                    self.get_page(PageType.Pending if captured.is_pending else PageType.Success)
                )
                return True

            if status == paypal_api.CaptureStatus.COMPLETED.name:
                captures = (
                    data.get("purchase_units", [{}])[0].get("payments", {}).get("captures", [])
                )
                if captures:
                    payment.set_in_pay(int(self.elid()), f"{order_id}/{captures[0]['id']}")
                logger.info(f"order {order_id} already completed, skipping new order")
                self.redirect_to_url(self.get_page(PageType.Success))
                return True

            return False
        except Exception as e:
            logger.warning(
                f"could not verify existing externalid {external_id},"
                f" creating a new order instead: {e}"
            )
            return False

    def content_type(self):
        return "text/html"


if __name__ == "__main__":
    run_cgi(PayPalPaymentCgi)
