#!/usr/bin/env python3
import json
import datetime
import sys
import urllib.error
import urllib.parse
import urllib.request
import uuid


if len(sys.argv) != 3:
    raise SystemExit("Usage: workshop_http_contract.py BASE_URL SERVICE_TOKEN")

BASE_URL = sys.argv[1].rstrip("/")
SERVICE_TOKEN = sys.argv[2]


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=20) 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():
    return str(uuid.uuid4())


def create_booking_fixture(prefix, booking_overrides=None):
    service_headers = {"X-Yokesen-Service-Token": SERVICE_TOKEN}
    _, handoff = call(
        "POST",
        "/api/v1/chat/workshop-opportunities",
        {
            "journey_id": f"journey-{prefix}",
            "chat_id": f"chat-{prefix}",
            "session_id": f"session-{prefix}",
        },
        {**service_headers, "Idempotency-Key": f"handoff-{prefix}"},
        (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)
    assert availability["slots"], f"{prefix} must receive an available 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()},
        (201,),
    )
    body = {
        "hold_id": held["hold"]["hold_id"],
        "pic_name": f"{prefix} User",
        "company_name": f"{prefix} Company",
        "role_title": "Director",
        "work_email": f"{prefix.lower()}@example.com",
        "phone": "+628000000002",
        "participant_count": 8,
        "visitor_timezone": "Asia/Jakarta",
        "website_locale": "en",
        "delivery_mode": "remote",
        "destination_country": "ID",
        "destination_city": "Jakarta",
        "location_summary": "Contract fixture",
        "contact_consent": True,
        "privacy_policy_version": "2026-07-18",
    }
    body.update(booking_overrides or {})
    _, created = call(
        "POST",
        "/api/v1/bookings",
        body,
        {**booking_headers, "Idempotency-Key": idem()},
        (201,),
    )
    return created, booking_headers


service_headers = {"X-Yokesen-Service-Token": SERVICE_TOKEN}
handoff_status, handoff = call(
    "POST",
    "/api/v1/chat/workshop-opportunities",
    {"journey_id": "journey-http-contract", "chat_id": "chat-http-contract", "session_id": "session-http-contract"},
    {**service_headers, "Idempotency-Key": "http-contract-handoff"},
    (201,),
)
booking_token = handoff["booking_context_token"]
booking_headers = {"X-Booking-Context-Token": booking_token}

_, config = call("GET", "/api/v1/workshop-booking/config", headers=booking_headers)
assert config["source"] == "platform_api"
assert {item["package_code"] for item in config["packages"]} == {
    "executive",
    "company_ai_workforce",
    "undecided",
}

query = urllib.parse.urlencode({"package_code": "executive", "visitor_timezone": "Asia/Jakarta"})
_, availability = call("GET", "/api/v1/workshop-booking/availability?" + query, headers=booking_headers)
assert availability["slots"], "availability must contain a real Platform API slot"
slot = availability["slots"][0]

hold_key = idem()
_, hold_response = call(
    "POST",
    "/api/v1/workshop-booking/holds",
    {"slot_id": slot["slot_id"], "package_code": "executive"},
    {**booking_headers, "Idempotency-Key": hold_key},
    (201,),
)
hold = hold_response["hold"]
assert hold["status"] == "active"

_, hold_replay = call(
    "POST",
    "/api/v1/workshop-booking/holds",
    {"slot_id": slot["slot_id"], "package_code": "executive"},
    {**booking_headers, "Idempotency-Key": hold_key},
)
assert hold_replay["idempotent_replay"] is True
assert hold_replay["hold"]["hold_id"] == hold["hold_id"]
_, held_availability = call("GET", "/api/v1/workshop-booking/availability?" + query, headers=booking_headers)
assert held_availability["active_hold"]["hold_id"] == hold["hold_id"]

_, competing_handoff = call(
    "POST",
    "/api/v1/chat/workshop-opportunities",
    {"journey_id": "journey-http-competitor", "session_id": "session-http-competitor"},
    {**service_headers, "Idempotency-Key": "http-contract-competing-handoff"},
    (201,),
)
competing_headers = {"X-Booking-Context-Token": competing_handoff["booking_context_token"]}
_, conflict = call(
    "POST",
    "/api/v1/workshop-booking/holds",
    {"slot_id": slot["slot_id"], "package_code": "executive"},
    {**competing_headers, "Idempotency-Key": idem()},
    (409,),
)
assert conflict["code"] == "slot_unavailable"

booking_body = {
    "hold_id": hold["hold_id"],
    "pic_name": "HTTP Contract User",
    "company_name": "YOKESEN Contract Test",
    "role_title": "Director",
    "work_email": "contract-test@example.com",
    "phone": "+628000000000",
    "participant_count": 8,
    "visitor_timezone": "Asia/Jakarta",
    "website_locale": "de",
    "delivery_mode": "onsite",
    "destination_country": "ID",
    "destination_city": "Jakarta",
    "location_summary": "Jakarta",
    "contact_consent": True,
    "privacy_policy_version": "2026-07-18",
}
booking_key = idem()
_, created_booking = call(
    "POST",
    "/api/v1/bookings",
    booking_body,
    {**booking_headers, "Idempotency-Key": booking_key},
    (201,),
)
booking_reference = created_booking["booking"]["booking_reference"]
assert created_booking["booking"]["status"] == "pending_confirmation"

_, replayed_booking = call(
    "POST",
    "/api/v1/bookings",
    booking_body,
    {**booking_headers, "Idempotency-Key": booking_key},
)
assert replayed_booking["idempotent_replay"] is True
assert replayed_booking["booking"]["booking_reference"] == booking_reference

_, restored_booking = call("GET", f"/api/v1/bookings/{booking_reference}", headers=booking_headers)
assert restored_booking["booking"]["status"] == "pending_confirmation"

confirm_key = idem()
confirmation_body = {
    "issued_by": "http-contract-ops",
    "package_code": "executive",
    "payment_rail": "bank_transfer_invoice",
    "invoice_currency": "IDR",
    "tax_amount": 0,
    "expires_in_days": 7,
}
_, confirmation = call(
    "POST",
    f"/api/v1/works/workshop-bookings/{booking_reference}/confirm",
    confirmation_body,
    {**service_headers, "Idempotency-Key": confirm_key},
    (201,),
)
checkout_token = confirmation["checkout_token"]
invoice_number = confirmation["invoice"]["invoice_number"]
assert confirmation["invoice"]["payment"]["rail"] == "bank_transfer_invoice"
assert "/de/workshop-checkout/" in confirmation["checkout_url"]

# Exact confirmation replay never rotates/revokes the already-delivered checkout token.
_, confirmation_replay = call(
    "POST",
    f"/api/v1/works/workshop-bookings/{booking_reference}/confirm",
    confirmation_body,
    {**service_headers, "Idempotency-Key": confirm_key},
)
assert confirmation_replay["idempotent_replay"] is True
assert confirmation_replay["invoice_id"] == confirmation["invoice_id"]
assert confirmation_replay["checkout_link_reissue_required"] is True
assert "checkout_token" not in confirmation_replay
assert "checkout_url" not in confirmation_replay

checkout_headers = {"X-Checkout-Token": checkout_token}
_, checkout = call("GET", "/api/v1/workshop-checkout/session?locale=de", headers=checkout_headers)
assert checkout["checkout"]["locale"] == "de"
assert checkout["checkout"]["offer"]["total_amount"] == 25_000_000

_, accepted = call(
    "POST",
    "/api/v1/workshop-checkout/accept",
    {"terms_accepted": True, "locale": "de"},
    checkout_headers,
)
assert accepted["checkout"]["offer"]["status"] == "accepted"
assert accepted["checkout"]["locale"] == "de"

works_list = call("GET", "/api/v1/works/workshop-bookings?status=payment_pending", headers=service_headers)[1]
matching = [item for item in works_list["bookings"] if item["booking_reference"] == booking_reference]
assert matching and matching[0]["invoice"]["invoice_number"] == invoice_number
assert matching[0]["invoice"]["invoice_id"] == confirmation["invoice_id"]
assert matching[0]["invoice"]["amount"] == 25_000_000
assert matching[0]["invoice"]["currency"] == "IDR"
assert matching[0]["invoice"]["latest_payment"] is None
assert matching[0]["contact"]["email"] == booking_body["work_email"]

# Operations can rotate a lost checkout link without rebuilding or mutating the offer.
old_checkout_headers = checkout_headers
_, rotated = call(
    "POST",
    f"/api/v1/works/workshop-invoices/{confirmation['invoice_id']}/checkout-link",
    {},
    service_headers,
)
assert rotated["previous_link_revoked"] is True
assert rotated["checkout_token"] != checkout_token
call("GET", "/api/v1/workshop-checkout/session?locale=de", headers=old_checkout_headers, expected=(401,))
checkout_token = rotated["checkout_token"]
checkout_headers = {"X-Checkout-Token": checkout_token}
_, rotated_checkout = call("GET", "/api/v1/workshop-checkout/session?locale=de", headers=checkout_headers)
assert rotated_checkout["checkout"]["offer"]["status"] == "accepted"

# Works uses the internal invoice id for controlled reconciliation actions.
invoice_uuid = confirmation["invoice_id"]
paid_key = idem()
paid_body = {"bank_reference": "BANK-HTTP-CONTRACT", "confirmed_by": "http-contract-finance"}
_, paid = call(
    "POST",
    f"/api/v1/works/workshop-invoices/{invoice_uuid}/mark-paid",
    paid_body,
    {**service_headers, "Idempotency-Key": paid_key},
)
assert paid["verified_paid"] is True
assert paid["idempotent_replay"] is False

_, paid_replay = call(
    "POST",
    f"/api/v1/works/workshop-invoices/{invoice_uuid}/mark-paid",
    paid_body,
    {**service_headers, "Idempotency-Key": paid_key},
)
assert paid_replay["verified_paid"] is True
assert paid_replay["idempotent_replay"] is True

_, payment_conflict = call(
    "POST",
    f"/api/v1/works/workshop-invoices/{invoice_uuid}/mark-paid",
    {"bank_reference": "DIFFERENT-BANK-REFERENCE", "confirmed_by": "http-contract-finance"},
    {**service_headers, "Idempotency-Key": idem()},
    (409,),
)
assert payment_conflict["code"] == "payment_already_exists"

_, status = call("GET", "/api/v1/workshop-checkout/status", headers=checkout_headers)
assert status["verified_paid"] is True
assert status["invoice_status"] == "paid"
assert status["booking_status"] == "paid"

paid_list = call("GET", "/api/v1/works/workshop-bookings?status=paid", headers=service_headers)[1]
paid_matching = [item for item in paid_list["bookings"] if item["booking_reference"] == booking_reference]
assert paid_matching[0]["invoice"]["latest_payment"]["verified_paid"] is True
assert paid_matching[0]["invoice"]["latest_payment"]["provider"] == "manual_bank_transfer"

# International pricing is country-neutral: blocking time is derived from total
# travel days and approved travel costs are added separately.
international, international_headers = create_booking_fixture(
    "InternationalContract",
    {
        "website_locale": "en",
        "delivery_mode": "onsite",
        "destination_country": "ZA",
        "destination_city": "Cape Town",
        "location_summary": "Cape Town, South Africa",
    },
)
international_ref = international["booking"]["booking_reference"]
international_management = international["management_token"]
_, international_issue = call(
    "POST",
    f"/api/v1/works/workshop-bookings/{international_ref}/confirm",
    {
        "issued_by": "http-contract-international-ops",
        "package_code": "executive",
        "payment_rail": "midtrans_core",
        "invoice_currency": "IDR",
        "tax_amount": 0,
        "expires_in_days": 7,
        "total_blocked_travel_days": 4,
        "delegation_size": 3,
        "approved_travel_line_items": [
            {"line_type": "travel_reimbursement", "description": "Return flights for three", "line_amount": 60000000},
            {"line_type": "accommodation_reimbursement", "description": "Accommodation for three", "line_amount": 30000000},
            {"line_type": "local_transport_reimbursement", "description": "Local transport", "line_amount": 10000000},
            {"line_type": "meals_reimbursement", "description": "Meals for three", "line_amount": 10000000},
        ],
    },
    {**service_headers, "Idempotency-Key": idem()},
    (201,),
)
international_token = international_issue["checkout_token"]
_, international_checkout = call(
    "GET",
    "/api/v1/workshop-checkout/session?locale=en",
    headers={"X-Checkout-Token": international_token},
)
international_offer = international_checkout["checkout"]["offer"]
assert international_offer["subtotal_amount"] == 610_000_000
blocking_lines = [line for line in international_offer["lines"] if line["line_type"] == "blocking_time_professional_fee"]
assert len(blocking_lines) == 1 and blocking_lines[0]["line_amount"] == 500_000_000
assert international_offer["terms"]["total_blocked_travel_days"] == 4
assert international_offer["terms"]["delegation_size"] == 3
assert international_offer["terms"]["travel_costs_borne_by_inviter"] is True

# Cancelling an unpaid issued invoice atomically revokes its offer and checkout,
# releases the slot, and cannot be followed by a charge.
_, cancelled = call(
    "POST",
    f"/api/v1/bookings/{international_ref}/cancel",
    {},
    {"X-Booking-Management-Token": international_management},
)
assert cancelled["booking"]["status"] == "cancelled"
_, cancelled_checkout = call(
    "GET",
    "/api/v1/workshop-checkout/session?locale=en",
    headers={"X-Checkout-Token": international_token},
)
assert cancelled_checkout["checkout"]["invoice_status"] == "cancelled"
assert cancelled_checkout["checkout"]["offer"]["status"] == "cancelled"
_, cancelled_charge = call(
    "POST",
    "/api/v1/workshop-checkout/charge",
    {"payment_method": "bank_transfer", "bank": "bca"},
    {"X-Checkout-Token": international_token, "Idempotency-Key": idem()},
    (409,),
)
assert cancelled_charge["code"] == "invoice_not_payable"

# Non-IDR checkout requires finance approval, a recent sourced FX rate, an exact
# IDR conversion, and explicit customer disclosure acceptance.
foreign, _ = create_booking_fixture("ForeignCurrencyContract")
foreign_ref = foreign["booking"]["booking_reference"]
foreign_management = foreign["management_token"]
fx_at = datetime.datetime.now(datetime.timezone.utc).replace(microsecond=0).isoformat().replace("+00:00", "Z")
_, invalid_fx = call(
    "POST",
    f"/api/v1/works/workshop-bookings/{foreign_ref}/confirm",
    {
        "issued_by": "http-contract-fx-ops",
        "package_code": "executive",
        "payment_rail": "midtrans_core",
        "invoice_currency": "EUR",
        "finance_approved": True,
        "approved_subtotal_amount": 25000,
        "tax_amount": 0,
        "charge_amount_idr": 499000000,
        "fx_rate": 20000,
        "fx_rate_source": "HTTP contract treasury rate",
        "fx_rate_at": fx_at,
    },
    {**service_headers, "Idempotency-Key": idem()},
    (422,),
)
assert "does not match" in invalid_fx["error"]
_, foreign_issue = call(
    "POST",
    f"/api/v1/works/workshop-bookings/{foreign_ref}/confirm",
    {
        "issued_by": "http-contract-fx-ops",
        "package_code": "executive",
        "payment_rail": "midtrans_core",
        "invoice_currency": "EUR",
        "finance_approved": True,
        "approved_subtotal_amount": 25000,
        "tax_amount": 0,
        "charge_amount_idr": 500000000,
        "fx_rate": 20000,
        "fx_rate_source": "HTTP contract treasury rate",
        "fx_rate_at": fx_at,
    },
    {**service_headers, "Idempotency-Key": idem()},
    (201,),
)
foreign_token = foreign_issue["checkout_token"]
foreign_headers = {"X-Checkout-Token": foreign_token}
_, foreign_checkout = call("GET", "/api/v1/workshop-checkout/session?locale=de", headers=foreign_headers)
assert foreign_checkout["checkout"]["offer"]["currency"] == "EUR"
assert foreign_checkout["checkout"]["offer"]["total_amount"] == 25000
assert foreign_checkout["checkout"]["payment"]["charge_currency"] == "IDR"
assert foreign_checkout["checkout"]["payment"]["charge_amount"] == 500_000_000
assert foreign_checkout["checkout"]["payment"]["fx_rate"] == 20000
call(
    "POST",
    "/api/v1/workshop-checkout/accept",
    {"terms_accepted": True, "locale": "de"},
    foreign_headers,
    (422,),
)
_, foreign_accepted = call(
    "POST",
    "/api/v1/workshop-checkout/accept",
    {"terms_accepted": True, "fx_disclosure_accepted": True, "locale": "de"},
    foreign_headers,
)
assert foreign_accepted["checkout"]["offer"]["status"] == "accepted"
call(
    "POST",
    f"/api/v1/bookings/{foreign_ref}/cancel",
    {},
    {"X-Booking-Management-Token": foreign_management},
)

print("workshop_http_contract: PASS")
