from datetime import date
from decimal import Decimal
from unittest.mock import patch
from app.models.enums import PolicyStatus
from app.services.audit_service import AuditService
from app.services.password_reset_service import PasswordResetService


def test_policy_update_creates_audit_log(client, auth_headers, db, seed_data):
    policy = seed_data["policy"]
    response = client.put(
        f"/api/v1/policies/{policy.id}",
        headers=auth_headers,
        json={"notes": "Updated via test", "total_commission": "1500.00"},
    )
    assert response.status_code == 200

    audit = client.get(
        "/api/v1/audit",
        headers=auth_headers,
        params={"entity_type": "policy", "entity_id": policy.id},
    )
    assert audit.status_code == 200
    fields = {row["field_name"] for row in audit.json()["items"]}
    assert "notes" in fields or "total_commission" in fields


def test_customer_update_creates_audit_log(client, auth_headers, seed_data):
    customer = seed_data["customer"]
    response = client.put(
        f"/api/v1/customers/{customer.id}",
        headers=auth_headers,
        json={"name": "Ramesh Kumar Updated", "mobile": customer.mobile},
    )
    assert response.status_code == 200

    audit = client.get(
        "/api/v1/audit",
        headers=auth_headers,
        params={"entity_type": "customer", "entity_id": customer.id},
    )
    assert audit.status_code == 200
    assert any(row["field_name"] == "name" for row in audit.json()["items"])


def test_forgot_password_and_reset(client, db, seed_data):
    with patch("app.utils.otp_delivery.send_otp_email"):
        forgot = client.post(
            "/api/v1/auth/forgot-password",
            json={"email": "admin@test.com", "channel": "email"},
        )
    assert forgot.status_code == 200
    assert "debug_otp" in forgot.json()

    otp = forgot.json()["debug_otp"]
    reset = client.post(
        "/api/v1/auth/reset-password",
        json={"email": "admin@test.com", "otp": otp, "new_password": "NewTest@123"},
    )
    assert reset.status_code == 200

    login = client.post(
        "/api/v1/auth/login",
        json={"email": "admin@test.com", "password": "NewTest@123"},
    )
    assert login.status_code == 200


def test_forgot_password_unknown_email(client):
    with patch("app.utils.otp_delivery.send_otp_email"):
        response = client.post(
            "/api/v1/auth/forgot-password",
            json={"email": "unknown@test.com", "channel": "email"},
        )
    assert response.status_code == 200
    assert response.json().get("debug_otp") is None


def test_reset_password_invalid_otp(client, seed_data):
    with patch("app.utils.otp_delivery.send_otp_email"):
        client.post("/api/v1/auth/forgot-password", json={"email": "admin@test.com", "channel": "email"})

    response = client.post(
        "/api/v1/auth/reset-password",
        json={"email": "admin@test.com", "otp": "000000", "new_password": "NewTest@123"},
    )
    assert response.status_code == 422


def test_audit_requires_auth(client, seed_data):
    response = client.get(
        "/api/v1/audit",
        params={"entity_type": "policy", "entity_id": seed_data["policy"].id},
    )
    assert response.status_code == 401


def test_virus_scan_disabled_by_default():
    from app.core.config import Settings

    settings = Settings(virus_scan_enabled=False)
    from app.utils.virus_scan import scan_file_bytes

    scan_file_bytes(b"%PDF-test", settings)


def test_virus_scan_rejects_threat():
    from app.core.config import Settings
    from app.core.exceptions import ValidationError
    from app.utils.virus_scan import scan_file_bytes

    settings = Settings(virus_scan_enabled=True, virus_scan_required=True)

    with patch("app.utils.virus_scan._clamd_instream_scan", side_effect=ValidationError("Upload rejected — threat detected: Eicar")):
        try:
            scan_file_bytes(b"bad", settings)
            assert False, "Expected ValidationError"
        except ValidationError as exc:
            assert "threat" in str(exc.message).lower()


def test_audit_service_log_update():
    svc = AuditService(db=None)
    before = {"name": "Old", "mobile": "9876543210"}
    after = {"name": "New", "mobile": "9876543210"}
    # smoke test serialization path
    from app.services.audit_service import _serialize

    assert _serialize(Decimal("100.50")) == "100.50"
    assert _serialize(date.today()) == date.today().isoformat()
    assert _serialize(PolicyStatus.ACTIVE) == "active"
