#!/usr/bin/env python3
"""Agent for testing business logic vulnerabilities during authorized assessments."""

import requests
import json
import argparse
import urllib3
import threading
from datetime import datetime
from urllib.parse import urljoin

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


def test_price_manipulation(base_url, token, cart_endpoint="/api/cart/add"):
    """Test price and quantity manipulation in purchase flows."""
    print("\n[*] Testing price/quantity manipulation...")
    findings = []
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    url = urljoin(base_url, cart_endpoint)
    test_cases = [
        {"name": "negative_quantity", "payload": {"product_id": 1, "quantity": -1}, "severity": "CRITICAL"},
        {"name": "zero_price", "payload": {"product_id": 1, "quantity": 1, "price": 0}, "severity": "CRITICAL"},
        {"name": "float_quantity", "payload": {"product_id": 1, "quantity": 0.001}, "severity": "HIGH"},
        {"name": "huge_quantity", "payload": {"product_id": 1, "quantity": 999999999}, "severity": "HIGH"},
        {"name": "negative_price", "payload": {"product_id": 1, "quantity": 1, "price": -99.99}, "severity": "CRITICAL"},
    ]
    for tc in test_cases:
        try:
            resp = requests.post(url, headers=headers, json=tc["payload"], timeout=10, verify=False)
            if resp.status_code in (200, 201):
                findings.append({
                    "type": "PRICE_MANIPULATION", "test": tc["name"],
                    "payload": tc["payload"], "status": resp.status_code,
                    "severity": tc["severity"],
                })
                print(f"  [!] {tc['name']}: Accepted (status {resp.status_code})")
            else:
                print(f"  [+] {tc['name']}: Rejected (status {resp.status_code})")
        except requests.RequestException as e:
            print(f"  [-] {tc['name']}: Error - {e}")
    return findings


def test_checkout_total_override(base_url, token, checkout_endpoint="/api/checkout"):
    """Test if the checkout total can be overridden in the request."""
    print("\n[*] Testing checkout total override...")
    findings = []
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    url = urljoin(base_url, checkout_endpoint)
    payloads = [
        {"cart_id": "test", "total": 0.01, "payment_method": "card"},
        {"cart_id": "test", "total": 0, "payment_method": "card"},
        {"cart_id": "test", "amount": 0.01},
    ]
    for payload in payloads:
        try:
            resp = requests.post(url, headers=headers, json=payload, timeout=10, verify=False)
            if resp.status_code in (200, 201):
                findings.append({
                    "type": "TOTAL_OVERRIDE", "payload": payload,
                    "status": resp.status_code, "severity": "CRITICAL",
                })
                print(f"  [!] Checkout accepted with total={payload.get('total', payload.get('amount'))}")
        except requests.RequestException:
            continue
    return findings


def test_coupon_reuse(base_url, token, coupon_endpoint="/api/cart/apply-coupon", code="DISCOUNT50"):
    """Test if a coupon can be applied multiple times."""
    print(f"\n[*] Testing coupon reuse ({code})...")
    findings = []
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    url = urljoin(base_url, coupon_endpoint)
    success_count = 0
    for i in range(5):
        try:
            resp = requests.post(url, headers=headers, json={"coupon_code": code},
                                 timeout=10, verify=False)
            if resp.status_code in (200, 201):
                success_count += 1
        except requests.RequestException:
            break
    if success_count > 1:
        findings.append({
            "type": "COUPON_REUSE", "code": code, "times_applied": success_count,
            "severity": "HIGH",
        })
        print(f"  [!] Coupon applied {success_count} times!")
    else:
        print(f"  [+] Coupon properly limited")
    return findings


def test_workflow_bypass(base_url, token, steps):
    """Test if workflow steps can be skipped."""
    print("\n[*] Testing workflow step bypass...")
    findings = []
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    for step in steps:
        url = urljoin(base_url, step["endpoint"])
        try:
            resp = requests.request(step.get("method", "POST"), url, headers=headers,
                                    json=step.get("payload", {}), timeout=10, verify=False)
            if resp.status_code in (200, 201):
                findings.append({
                    "type": "WORKFLOW_BYPASS", "step": step["name"],
                    "endpoint": step["endpoint"], "status": resp.status_code,
                    "severity": "HIGH",
                })
                print(f"  [!] Step '{step['name']}' bypassed (status {resp.status_code})")
            else:
                print(f"  [+] Step '{step['name']}' enforced (status {resp.status_code})")
        except requests.RequestException:
            continue
    return findings


def test_race_condition(base_url, token, endpoint, payload, concurrent=10):
    """Test for race conditions by sending concurrent requests."""
    print(f"\n[*] Testing race condition on {endpoint} ({concurrent} concurrent requests)...")
    findings = []
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    url = urljoin(base_url, endpoint)
    results = []

    def send_request():
        try:
            resp = requests.post(url, headers=headers, json=payload, timeout=10, verify=False)
            results.append({"status": resp.status_code, "body": resp.text[:200]})
        except requests.RequestException:
            results.append({"status": 0, "body": "error"})

    threads = [threading.Thread(target=send_request) for _ in range(concurrent)]
    for t in threads:
        t.start()
    for t in threads:
        t.join()

    successes = sum(1 for r in results if r["status"] in (200, 201))
    if successes > 1:
        findings.append({
            "type": "RACE_CONDITION", "endpoint": endpoint,
            "concurrent": concurrent, "successes": successes, "severity": "CRITICAL",
        })
        print(f"  [!] {successes}/{concurrent} requests succeeded (potential race condition)")
    else:
        print(f"  [+] {successes}/{concurrent} succeeded (properly serialized)")
    return findings


def test_self_referral(base_url, token, referral_endpoint="/api/referrals/invite", email="self@test.com"):
    """Test if self-referral is possible."""
    print("\n[*] Testing self-referral...")
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    url = urljoin(base_url, referral_endpoint)
    try:
        resp = requests.post(url, headers=headers, json={"referral_email": email},
                             timeout=10, verify=False)
        if resp.status_code in (200, 201):
            print(f"  [!] Self-referral accepted")
            return [{"type": "SELF_REFERRAL", "severity": "MEDIUM"}]
        else:
            print(f"  [+] Self-referral blocked (status {resp.status_code})")
    except requests.RequestException:
        pass
    return []


def generate_report(findings, output_path):
    """Generate business logic vulnerability report."""
    report = {
        "assessment_date": datetime.now().isoformat(),
        "total_findings": len(findings),
        "by_type": {},
        "findings": findings,
    }
    for f in findings:
        t = f.get("type", "UNKNOWN")
        report["by_type"][t] = report["by_type"].get(t, 0) + 1
    with open(output_path, "w") as fh:
        json.dump(report, fh, indent=2)
    print(f"\n[*] Report: {output_path} | Findings: {len(findings)}")


def main():
    parser = argparse.ArgumentParser(description="Business Logic Vulnerability Testing Agent")
    parser.add_argument("base_url", help="Base URL of the target application")
    parser.add_argument("--token", required=True, help="Bearer token for authentication")
    parser.add_argument("--cart-endpoint", default="/api/cart/add")
    parser.add_argument("--checkout-endpoint", default="/api/checkout")
    parser.add_argument("--coupon-code", default="DISCOUNT50")
    parser.add_argument("-o", "--output", default="business_logic_report.json")
    args = parser.parse_args()

    print(f"[*] Business Logic Vulnerability Assessment: {args.base_url}")
    findings = []
    findings.extend(test_price_manipulation(args.base_url, args.token, args.cart_endpoint))
    findings.extend(test_checkout_total_override(args.base_url, args.token, args.checkout_endpoint))
    findings.extend(test_coupon_reuse(args.base_url, args.token, code=args.coupon_code))
    findings.extend(test_race_condition(args.base_url, args.token,
                                        args.cart_endpoint, {"coupon_code": args.coupon_code}))
    findings.extend(test_self_referral(args.base_url, args.token))
    generate_report(findings, args.output)


if __name__ == "__main__":
    main()
