#!/usr/bin/env python3
"""Real Midtrans sandbox canary for the workshop payment round-trip."""

import json
import os
import pathlib
import sys
import urllib.error
import urllib.parse
import urllib.request
import uuid


if len(sys.argv) != 5:
    raise SystemExit(
        "Usage: workshop_midtrans_sandbox_canary.py BASE_URL SERVICE_TOKEN STATE_FILE create|verify"
    )

BASE_URL = sys.argv[1].rstrip("/")
SERVICE_TOKEN = sys.argv[2]
STATE_FILE = pathlib.Path(sys.argv[3])
PHASE = sys.argv[4]


def call(method, path, body=None, headers=None, expected=(200,)):
    payload = None if body is None else json.dumps(body).encode("utf-8")
    request_headers = {"Accept": "application/json"}
    if body is not None:
        request_headers["Content-Type"] = "application/json"
    request_headers.update(headers or {})
    request = urllib.request.Request(
        BASE_URL + path,
        data=payload,
        headers=request_headers,
        method=method,
    )
    try:
        with urllib.request.urlopen(request, timeout=30) as response:
            status = response.status
            decoded = json.loads(response.read().decode("utf-8"))
    except urllib.error.HTTPError as error:
        status = error.code
        decoded = json.loads(error.read().decode("utf-8"))
    if status not in expected:
        raise AssertionError(f"{method} {path}: expected {expected}, got {status}: {decoded}")
    return status, decoded


def idem(prefix):
    return f"sandbox-{prefix}-{uuid.uuid4()}"


def create_canary():
    unique = uuid.uuid4().hex[:12]
    service_headers = {"X-Yokesen-Service-Token": SERVICE_TOKEN}
    _, handoff = call(
        "POST",
        "/api/v1/chat/workshop-opportunities",
        {
            "journey_id": f"journey-sandbox-{unique}",
            "chat_id": f"chat-sandbox-{unique}",
            "session_id": f"session-sandbox-{unique}",
        },
        {**service_headers, "Idempotency-Key": idem("handoff")},
        (201,),
    )
    booking_headers = {"X-Booking-Context-Token": handoff["booking_context_token"]}
    query = urllib.parse.urlencode(
        {"package_code": "executive", "visitor_timezone": "Asia/Jakarta"}
    )
    _, availability = call(
        "GET", "/api/v1/workshop-booking/availability?" + query, headers=booking_headers
    )
    if not availability["slots"]:
        raise AssertionError("Sandbox canary did not receive a Platform slot")
    _, held = call(
        "POST",
        "/api/v1/workshop-booking/holds",
        {"slot_id": availability["slots"][0]["slot_id"], "package_code": "executive"},
        {**booking_headers, "Idempotency-Key": idem("hold")},
        (201,),
    )
    _, created = call(
        "POST",
        "/api/v1/bookings",
        {
            "hold_id": held["hold"]["hold_id"],
            "pic_name": "YOKESEN Sandbox Canary",
            "company_name": "YOKESEN Internal Verification",
            "role_title": "Quality Assurance",
            "work_email": "sandbox-canary@example.com",
            "phone": "+628000000000",
            "participant_count": 4,
            "visitor_timezone": "Asia/Jakarta",
            "website_locale": "id",
            "delivery_mode": "remote",
            "destination_country": "ID",
            "destination_city": "Jakarta",
            "location_summary": "Remote sandbox canary",
            "contact_consent": True,
            "privacy_policy_version": "2026-07-18",
        },
        {**booking_headers, "Idempotency-Key": idem("booking")},
        (201,),
    )
    booking_reference = created["booking"]["booking_reference"]
    _, issued = call(
        "POST",
        f"/api/v1/works/workshop-bookings/{booking_reference}/confirm",
        {
            "issued_by": "sandbox-canary-operations",
            "package_code": "executive",
            "payment_rail": "midtrans_core",
            "invoice_currency": "IDR",
            "tax_amount": 0,
            "expires_in_days": 1,
        },
        {**service_headers, "Idempotency-Key": idem("confirm")},
        (201,),
    )
    checkout_token = issued["checkout_token"]
    checkout_headers = {"X-Checkout-Token": checkout_token}
    _, checkout = call("GET", "/api/v1/workshop-checkout/session?locale=id", headers=checkout_headers)
    if checkout["checkout"]["payment"]["midtrans_mode"] != "sandbox":
        raise AssertionError("Canary is not using Midtrans sandbox mode")
    _, accepted = call(
        "POST",
        "/api/v1/workshop-checkout/accept",
        {"terms_accepted": True, "locale": "id"},
        checkout_headers,
    )
    if accepted["checkout"]["offer"]["status"] != "accepted":
        raise AssertionError("Workshop offer was not accepted")

    charge_key = idem("charge")
    charge_body = {"payment_method": "bank_transfer", "bank": "bca"}
    _, charged = call(
        "POST",
        "/api/v1/workshop-checkout/charge",
        charge_body,
        {**checkout_headers, "Idempotency-Key": charge_key},
        (201,),
    )
    payment = charged["payment"]
    if payment["status"] != "pending" or payment["verified_paid"] is not False:
        raise AssertionError(f"Unexpected initial Midtrans state: {payment}")
    virtual_accounts = payment.get("virtual_accounts") or []
    bca_accounts = [item for item in virtual_accounts if item.get("bank") == "bca"]
    if len(bca_accounts) != 1 or not bca_accounts[0].get("va_number"):
        raise AssertionError("Midtrans sandbox did not return one BCA virtual account")

    _, replay = call(
        "POST",
        "/api/v1/workshop-checkout/charge",
        charge_body,
        {**checkout_headers, "Idempotency-Key": charge_key},
    )
    if replay["idempotent_replay"] is not True or replay["payment"]["payment_id"] != payment["payment_id"]:
        raise AssertionError("Exact charge replay did not return the original payment")

    _, active_reuse = call(
        "POST",
        "/api/v1/workshop-checkout/charge",
        charge_body,
        {**checkout_headers, "Idempotency-Key": idem("charge-retry")},
    )
    if active_reuse.get("active_attempt_reused") is not True:
        raise AssertionError("A retry created a second active Midtrans attempt")

    _, checkout_status = call("GET", "/api/v1/workshop-checkout/status", headers=checkout_headers)
    if checkout_status["verified_paid"] is not False or checkout_status["payment"]["status"] != "pending":
        raise AssertionError("Checkout did not preserve pending/webhook-pending truth")

    state = {
        "booking_reference": booking_reference,
        "checkout_token": checkout_token,
        "invoice_id": issued["invoice_id"],
        "payment_id": payment["payment_id"],
        "va_number": bca_accounts[0]["va_number"],
    }
    STATE_FILE.write_text(json.dumps(state), encoding="utf-8")
    print(
        json.dumps(
            {
                "phase": "ready_for_simulator",
                "bank": "bca",
                "va_number": state["va_number"],
                "booking_reference": booking_reference,
                "payment_id": payment["payment_id"],
            }
        )
    )


def prepare_card_canary():
    unique = uuid.uuid4().hex[:12]
    service_headers = {"X-Yokesen-Service-Token": SERVICE_TOKEN}
    _, handoff = call(
        "POST",
        "/api/v1/chat/workshop-opportunities",
        {
            "journey_id": f"journey-card-sandbox-{unique}",
            "chat_id": f"chat-card-sandbox-{unique}",
            "session_id": f"session-card-sandbox-{unique}",
        },
        {**service_headers, "Idempotency-Key": idem("card-handoff")},
        (201,),
    )
    booking_headers = {"X-Booking-Context-Token": handoff["booking_context_token"]}
    query = urllib.parse.urlencode(
        {"package_code": "executive", "visitor_timezone": "Asia/Jakarta"}
    )
    _, availability = call(
        "GET", "/api/v1/workshop-booking/availability?" + query, headers=booking_headers
    )
    if not availability["slots"]:
        raise AssertionError("Card sandbox canary did not receive a Platform slot")
    _, held = call(
        "POST",
        "/api/v1/workshop-booking/holds",
        {"slot_id": availability["slots"][0]["slot_id"], "package_code": "executive"},
        {**booking_headers, "Idempotency-Key": idem("card-hold")},
        (201,),
    )
    _, created = call(
        "POST",
        "/api/v1/bookings",
        {
            "hold_id": held["hold"]["hold_id"],
            "pic_name": "YOKESEN Card Sandbox Canary",
            "company_name": "YOKESEN Internal Verification",
            "role_title": "Quality Assurance",
            "work_email": "card-sandbox-canary@example.com",
            "phone": "+628000000000",
            "participant_count": 4,
            "visitor_timezone": "Asia/Jakarta",
            "website_locale": "en",
            "delivery_mode": "remote",
            "destination_country": "ID",
            "destination_city": "Jakarta",
            "location_summary": "Remote card sandbox canary",
            "contact_consent": True,
            "privacy_policy_version": "2026-07-18",
        },
        {**booking_headers, "Idempotency-Key": idem("card-booking")},
        (201,),
    )
    booking_reference = created["booking"]["booking_reference"]
    _, issued = call(
        "POST",
        f"/api/v1/works/workshop-bookings/{booking_reference}/confirm",
        {
            "issued_by": "sandbox-card-canary-operations",
            "package_code": "executive",
            "payment_rail": "midtrans_core",
            "invoice_currency": "IDR",
            "tax_amount": 0,
            "expires_in_days": 1,
        },
        {**service_headers, "Idempotency-Key": idem("card-confirm")},
        (201,),
    )
    checkout_token = issued["checkout_token"]
    checkout_headers = {"X-Checkout-Token": checkout_token}
    _, checkout = call("GET", "/api/v1/workshop-checkout/session?locale=en", headers=checkout_headers)
    payment_context = checkout["checkout"]["payment"]
    if payment_context["midtrans_mode"] != "sandbox" or not payment_context["midtrans_client_key"]:
        raise AssertionError("Card canary did not receive a sandbox Midtrans client key")
    _, accepted = call(
        "POST",
        "/api/v1/workshop-checkout/accept",
        {"terms_accepted": True, "locale": "en"},
        checkout_headers,
    )
    if accepted["checkout"]["offer"]["status"] != "accepted":
        raise AssertionError("Card canary offer was not accepted")
    state = {
        "booking_reference": booking_reference,
        "checkout_token": checkout_token,
        "invoice_id": issued["invoice_id"],
        "midtrans_client_key": payment_context["midtrans_client_key"],
        "charge_amount": payment_context["charge_amount"],
    }
    STATE_FILE.write_text(json.dumps(state), encoding="utf-8")
    print(
        json.dumps(
            {
                "phase": "ready_for_card_tokenization",
                "booking_reference": booking_reference,
                "midtrans_client_key": state["midtrans_client_key"],
                "charge_amount": state["charge_amount"],
            }
        )
    )


def charge_card_canary():
    state = json.loads(STATE_FILE.read_text(encoding="utf-8"))
    token_id = os.environ.get("MIDTRANS_CARD_TOKEN", "").strip()
    if not token_id:
        raise AssertionError("MIDTRANS_CARD_TOKEN is required for the card charge phase")
    checkout_headers = {"X-Checkout-Token": state["checkout_token"]}
    charge_key = idem("card-charge")
    charge_body = {"payment_method": "credit_card", "token_id": token_id}
    _, charged = call(
        "POST",
        "/api/v1/workshop-checkout/charge",
        charge_body,
        {**checkout_headers, "Idempotency-Key": charge_key},
        (201,),
    )
    payment = charged["payment"]
    if payment["status"] not in ("pending", "challenge") or not payment.get("redirect_url"):
        raise AssertionError(f"Card charge did not enter 3DS challenge: {payment}")
    _, replay = call(
        "POST",
        "/api/v1/workshop-checkout/charge",
        charge_body,
        {**checkout_headers, "Idempotency-Key": charge_key},
    )
    if replay["idempotent_replay"] is not True or replay["payment"]["payment_id"] != payment["payment_id"]:
        raise AssertionError("Card charge replay did not preserve the original attempt")
    state.update({"payment_id": payment["payment_id"], "redirect_url": payment["redirect_url"]})
    STATE_FILE.write_text(json.dumps(state), encoding="utf-8")
    print(
        json.dumps(
            {
                "phase": "ready_for_3ds",
                "payment_id": state["payment_id"],
                "redirect_url": state["redirect_url"],
            }
        )
    )


def verify_card_canary():
    state = json.loads(STATE_FILE.read_text(encoding="utf-8"))
    service_headers = {"X-Yokesen-Service-Token": SERVICE_TOKEN}
    checkout_headers = {"X-Checkout-Token": state["checkout_token"]}
    _, reconciled = call(
        "POST",
        f"/api/v1/works/workshop-payments/{state['payment_id']}/reconcile",
        {},
        service_headers,
    )
    if reconciled["provider_status_verified"] is not True or reconciled["payment"]["status"] != "paid":
        raise AssertionError(f"3DS card payment did not reconcile as paid: {reconciled}")
    _, status = call("GET", "/api/v1/workshop-checkout/status", headers=checkout_headers)
    if status["verified_paid"] is not True or status["invoice_status"] != "paid":
        raise AssertionError(f"Card checkout did not restore paid truth: {status}")
    _, paid_list = call(
        "GET", "/api/v1/works/workshop-bookings?status=paid", headers=service_headers
    )
    matching = [
        item
        for item in paid_list["bookings"]
        if item["booking_reference"] == state["booking_reference"]
    ]
    if not matching:
        raise AssertionError("Works did not receive the paid 3DS card booking")
    latest = matching[0]["invoice"]["latest_payment"]
    if latest["verified_paid"] is not True or latest["payment_method"] != "credit_card":
        raise AssertionError("Works card payment audit state is incomplete")
    print("workshop_midtrans_card_sandbox_canary: PASS")


def verify_canary():
    state = json.loads(STATE_FILE.read_text(encoding="utf-8"))
    service_headers = {"X-Yokesen-Service-Token": SERVICE_TOKEN}
    checkout_headers = {"X-Checkout-Token": state["checkout_token"]}
    _, reconciled = call(
        "POST",
        f"/api/v1/works/workshop-payments/{state['payment_id']}/reconcile",
        {},
        service_headers,
    )
    if reconciled["provider_status_verified"] is not True or reconciled["payment"]["status"] != "paid":
        raise AssertionError(f"Provider reconciliation did not verify paid: {reconciled}")

    _, status = call("GET", "/api/v1/workshop-checkout/status", headers=checkout_headers)
    if status["verified_paid"] is not True:
        raise AssertionError(f"Checkout was not restored as verified paid: {status}")
    if status["invoice_status"] != "paid" or status["booking_status"] != "paid":
        raise AssertionError("Invoice and booking did not converge to paid")

    _, paid_list = call(
        "GET", "/api/v1/works/workshop-bookings?status=paid", headers=service_headers
    )
    matching = [
        item
        for item in paid_list["bookings"]
        if item["booking_reference"] == state["booking_reference"]
    ]
    if not matching or matching[0]["invoice"]["latest_payment"]["verified_paid"] is not True:
        raise AssertionError("Works did not receive the verified paid state")

    _, blocked_link = call(
        "POST",
        f"/api/v1/works/workshop-invoices/{state['invoice_id']}/checkout-link",
        {},
        service_headers,
        (409,),
    )
    if blocked_link.get("code") != "invoice_not_payable":
        raise AssertionError("Paid invoice unexpectedly allowed checkout-link rotation")

    _, blocked_charge = call(
        "POST",
        "/api/v1/workshop-checkout/charge",
        {"payment_method": "bank_transfer", "bank": "bca"},
        {**checkout_headers, "Idempotency-Key": idem("paid-charge")},
        (409,),
    )
    if blocked_charge.get("code") != "invoice_not_payable":
        raise AssertionError("Paid invoice unexpectedly allowed another charge")

    print("workshop_midtrans_sandbox_canary: PASS")


if PHASE == "create":
    create_canary()
elif PHASE == "verify":
    verify_canary()
elif PHASE == "card_prepare":
    prepare_card_canary()
elif PHASE == "card_charge":
    charge_card_canary()
elif PHASE == "card_verify":
    verify_card_canary()
else:
    raise SystemExit("Phase must be create, verify, card_prepare, card_charge, or card_verify")
