#!/usr/bin/python3

import sys
from os import EX_OK, EX_UNAVAILABLE
from xml.etree import ElementTree

sys.path.insert(0, "/usr/local/mgr5/lib/python")
from billmgr.modules.paymentcgi import PageType, PaymentCgi, PaymentCgiType, run_cgi, InputContentType, process_test_cgi_run
import billmgr.logger as logging
import billmgr.db as db
import billmgr.payment as payment
import billmgr.exception
# from billmgr.exception import log_backtrace


from payssion.api import PayssionAPI
from payssion.enums import PaymentStatus


MODULE = "payssionnotify"
logging.init_logging(MODULE)
logger = logging.get_logger(MODULE)

PAYMETHOD_MODULE_NAME = "pmpayssion"


class PayssionNotifyCgi(PaymentCgi):

    def need_auth(self):
        return False
    
    @property
    def input_content_type(self):
        return InputContentType.Json

    def __init__(self):
        self._payment_id = 0
        PaymentCgi.__init__(self)

    def cgi_type(self) -> PaymentCgiType:
        return PaymentCgiType.Payment

    def _find_payment(self) -> db.Record:
        return db.get_first_record_unwrap(
            f" SELECT pay.externalid, pm.xmlparams FROM payment AS pay"
            f" JOIN paymethod AS pm ON pay.paymethod = pm.id"
            f" WHERE pm.module = '{PAYMETHOD_MODULE_NAME}'"
            f" AND pay.id = \"{self.elid()}\""
        )

    def parse_input(self): # type: ignore
        self.input["elid"] = self.input.get("order_id").split('/')[-1] 
        return self.input

    def process(self):
        logger.info("run payssionnotify")
        logger.info(f"param: {self.paymethod_params}")
        logger.info(f"payment: {self.payment_params}")
        _payment = self._find_payment()
        params = ElementTree.fromstring(_payment.as_str("xmlparams"))
        transaction_id = self.payment_params.get("externalid")

        api_key: str = params.findtext("api_key", default="")
        self.input["api_key"] = api_key
        secret_key: str = params.findtext("secret_key", default="")
        is_test: bool = False if params.findtext(
            "test", default="") == "off" else True
        api = PayssionAPI(api_key, secret_key, is_test)

        try:

            notification_sig = api.get_sig(self.input, "notification")
            if notification_sig != self.input["notify_sig"]:
                raise billmgr.exception.XmlException("payssion_not_equal_sig_for_notify")

            status = self.input.get("state")
            logger.info(f"status is {status}")

            if status == PaymentStatus.COMPLETED.value:
                payment.set_paid(int(self.elid()), transaction_id)
                logger.info("paid")
            elif status in {PaymentStatus.PENDING.value,
                            PaymentStatus.AWAITING_CONFIRM.value
                            }:
                payment.set_in_pay(int(self.elid()), transaction_id)
                logger.info("in_pay")
            elif status == PaymentStatus.PAID_PARTIAL.value:
                api.make_refund(transaction_id, self.input["order_id"])
                payment.set_canceled(int(self.elid()), transaction_id)
                logger.info("refund partial payment")
            elif status in {PaymentStatus.FAILED.value, 
                            PaymentStatus.CANCELLED.value,
                            PaymentStatus.EXPIRED.value,
                            PaymentStatus.DISPUTED.value,
                            PaymentStatus.REJECTED.value}:
                payment.set_canceled(int(self.elid()), transaction_id)
                logger.info("canceled")

        except Exception:
            billmgr.exception.log_backtrace()
            self.redirect_to_url(self.get_page(PageType.Fail))


if __name__ == "__main__":
    process_test_cgi_run()

    exit_code: int = EX_OK

    try:
        run_cgi(PayssionNotifyCgi)
    except Exception:
        billmgr.exception.log_backtrace()
        exit_code = EX_UNAVAILABLE

    sys.exit(exit_code)


