from __future__ import annotations

from io import BytesIO
from zipfile import ZipFile, ZipInfo

import pytest

from jarvis_finance.imports import postfinance_documents as parser


def overview_text() -> str:
    lines = [
        "Portfolio  Stand 27/07/2026 09:14:30",
        "Offene Aufträge 0",
        "EUR  1.0000  100.00  x  x  100.00  x",
        "USD  1.0000  100.00  x  x  100.00  x",
        "Gesamt CHF  200.00  2'200.00  2'400.00",
        "Produkt  Anzahl  Einstandskurs  Einstandswert  Differenz  Kurs  x  Wert CHF  Anteil %",
        "Aktien",
    ]
    for index in range(11):
        lines.append(f"Synthetic Asset {index + 1:02d}  1  50  50  -  100 CHF  x  100  4.17")
    lines.extend(["Zwischensumme Aktien in CHF  0  0  1'100.00", "ETFs"])
    for index in range(11, 22):
        lines.append(f"Synthetic Asset {index + 1:02d}  1  50  50  -  100 CHF  x  100  4.17")
    lines.append("Zwischensumme ETFs in CHF  0  0  1'100.00")
    return "\n".join(lines)


def trade_text() -> str:
    return """Transaktionsbeleg
Börsentransaktion: Kauf
Unsere Referenz: 100
Kontonummer: 123456
Ausführungsdatum: 01.07.2026
Valutadatum: 03.07.2026
Synthetic Asset 01 ISIN: CH0000000000
Anzahl  Preis  Betrag
1  50  CHF  50
Total CHF 50
"""


def zip_bytes(name: str = "20260701_Borsenabrechnung_100.pdf", raw: bytes = b"%PDF-trade") -> bytes:
    output = BytesIO()
    with ZipFile(output, "w") as archive:
        archive.writestr(name, raw)
    return output.getvalue()


def test_synthetic_zip_pdf_bundle_is_parsed_and_reconciled(monkeypatch):
    monkeypatch.setattr(parser, "_pdf_text", lambda raw: (trade_text(), 1) if raw == b"%PDF-trade" else (overview_text(), 5))
    result = parser.parse_postfinance_bundle(zip_bytes(), b"%PDF-overview")
    assert len(result.documents) == 1
    assert len(result.events) == 1 and result.events[0].event_type == "buy"
    assert len(result.snapshot.positions) == 22 and len(result.snapshot.cash) == 2
    assert result.snapshot.total_value_chf == result.snapshot.securities_total_chf + result.snapshot.cash_total_chf
    assert result.snapshot.open_orders == 0


def test_zip_path_traversal_and_symlink_are_rejected(monkeypatch):
    monkeypatch.setattr(parser, "_pdf_text", lambda _raw: (overview_text(), 1))
    with pytest.raises(ValueError, match="unsafe"):
        parser.parse_postfinance_bundle(zip_bytes("../escape.pdf"), b"%PDF-overview")
    output = BytesIO()
    with ZipFile(output, "w") as archive:
        info = ZipInfo("link.pdf")
        info.external_attr = (0o120777 << 16)
        archive.writestr(info, b"%PDF-link")
    with pytest.raises(ValueError, match="unsafe"):
        parser.parse_postfinance_bundle(output.getvalue(), b"%PDF-overview")


def test_preview_parser_rejects_incomplete_or_non_pdf_sources(monkeypatch):
    monkeypatch.setattr(parser, "_pdf_text", lambda _raw: (overview_text(), 5))
    with pytest.raises(ValueError, match="unsupported entry"):
        parser.parse_postfinance_bundle(zip_bytes("note.txt"), b"%PDF-overview")
    monkeypatch.setattr(parser, "_pdf_text", lambda raw: (trade_text(), 1) if raw == b"%PDF-trade" else ("27/07/2026 09:14:30\nno positions", 1))
    with pytest.raises(ValueError, match="complete snapshot"):
        parser.parse_postfinance_bundle(zip_bytes(), b"%PDF-overview")
