from __future__ import annotations

from calendar import monthrange
from datetime import date, timedelta
from decimal import Decimal
import re
from sqlite3 import Connection
from typing import Any

from jarvis_finance.services.cash_service import authoritative_cash_movements
from jarvis_finance.services.daily_valuations import SOURCE_KEY as CRYPTO_VALUATION_SOURCE

LEGACY_CRYPTO_VALUATION_SOURCE = "daily_crypto_current_valuation_v1"

MONEY = Decimal("0.01")
PERCENT = Decimal("0.0001")
MODEL_PERIODS = {"since_anchor", "1m", "3m", "ytd", "1y", "all"}
SNAPSHOT_PRECEDENCE = {
    "reconciliation": 4,
    "manual_balance": 3,
    "csv_anchor_balance": 2,
    "calculated_balance": 1,
}
COMPONENT_LABELS = {
    "postfinance": "PostFinance",
    "truewealth": "True Wealth",
    "crypto": "Krypto",
    "bank_cash": "Bankguthaben",
    "other_assets": "Weitere Anlagen",
}


def _money(value: Decimal | None) -> str | None:
    return None if value is None else format(value.quantize(MONEY), "f")


def _subtract_months(day: date, months: int) -> date:
    absolute = day.year * 12 + day.month - 1 - months
    year, month_index = divmod(absolute, 12)
    month = month_index + 1
    return date(year, month, min(day.day, monthrange(year, month)[1]))


def _authoritative_cash_movements(
    conn: Connection, *, account_id: str, after: str | None, through: str
) -> dict[str, Any]:
    return authoritative_cash_movements(
        conn, account_id=account_id, after=after, through=through
    )


def effective_cash_evidence(
    conn: Connection, *, account_id: str, as_of: str
) -> dict[str, Any]:
    """Return one account's effective cash evidence without writing.

    The newest business date wins.  If several valid snapshots exist on that
    date, the deterministic precedence is reconciliation > manual > CSV anchor.
    Confirmed movements are then applied forward only.  With no anchor, a
    non-empty confirmed ledger may provide a calculated balance; empty evidence
    is unavailable rather than a false CHF 0.
    """

    snapshot = conn.execute(
        """SELECT * FROM cash_account_snapshots
             WHERE account_id=? AND balance_date<=? AND amount_chf IS NOT NULL
             ORDER BY balance_date DESC,
               CASE snapshot_type
                 WHEN 'reconciliation' THEN 4
                 WHEN 'manual_balance' THEN 3
                 WHEN 'csv_anchor_balance' THEN 2
                 WHEN 'calculated_balance' THEN 1
                 ELSE 0 END DESC,
               created_at DESC,snapshot_id DESC LIMIT 1""",
        (account_id, as_of),
    ).fetchone()
    if snapshot:
        anchor_day = str(snapshot["balance_date"])
        anchor = Decimal(str(snapshot["amount_chf"]))
        movement_evidence = _authoritative_cash_movements(
            conn, account_id=account_id, after=anchor_day, through=as_of
        )
        movement = movement_evidence["amount"]
        return {
            "account_id": account_id,
            "anchor_type": str(snapshot["snapshot_type"]),
            "anchor_date": anchor_day,
            "anchor_value_chf": _money(anchor),
            "movement_chf": _money(movement),
            "value_chf": _money(anchor + movement),
            "quality": "confirmed" if anchor_day == as_of and not movement else "carried",
            "source_date": movement_evidence["last_date"] or anchor_day,
            "movement_source": movement_evidence["source"],
        }

    movement_evidence = _authoritative_cash_movements(
        conn, account_id=account_id, after=None, through=as_of
    )
    if movement_evidence["count"]:
        value = movement_evidence["amount"]
        return {
            "account_id": account_id,
            "anchor_type": "calculated_balance",
            "anchor_date": None,
            "anchor_value_chf": None,
            "movement_chf": _money(value),
            "value_chf": _money(value),
            "quality": "modelled",
            "source_date": movement_evidence["last_date"],
            "movement_source": movement_evidence["source"],
        }
    return {
        "account_id": account_id,
        "anchor_type": None,
        "anchor_date": None,
        "anchor_value_chf": None,
        "movement_chf": None,
        "value_chf": None,
        "quality": "unavailable",
        "source_date": None,
        "movement_source": None,
    }


def _role_account_ids(conn: Connection, role: str) -> list[str]:
    return [
        str(row[0])
        for row in conn.execute(
            """SELECT a.account_id FROM accounts a
                 JOIN performance_scope_classifications psc ON psc.account_id=a.account_id
                WHERE a.is_active=1 AND psc.classification_role=?
                  AND psc.decision_version='investment_performance_scope_v1'
                ORDER BY a.account_id""",
            (role,),
        ).fetchall()
    ]


def _latest_model_rows(
    conn: Connection,
    *,
    account_ids: list[str],
    through: str,
    source: str | None = None,
    source_prefix: str | None = None,
) -> list[dict[str, Any]]:
    if not account_ids:
        return []
    placeholders = ",".join("?" for _ in account_ids)
    if source and source_prefix:
        raise ValueError("source and source_prefix are mutually exclusive")
    source_sql = " AND source=?" if source else " AND source LIKE ?" if source_prefix else ""
    params: list[Any] = [*account_ids, through]
    if source:
        params.append(source)
    elif source_prefix:
        params.append(f"{source_prefix}%")
    rows = conn.execute(
        f"""WITH ranked AS (
              SELECT scope_id,substr(valuation_at,1,10) day,value_original,currency,
                     fx_rate_to_base,source,snapshot_version,captured_at,snapshot_id,
                     ROW_NUMBER() OVER(
                       PARTITION BY scope_id,substr(valuation_at,1,10)
                       ORDER BY snapshot_version DESC,captured_at DESC,snapshot_id DESC
                     ) rn
                FROM portfolio_valuation_snapshots
               WHERE scope_kind='account' AND scope_id IN ({placeholders})
                 AND substr(valuation_at,1,10)<=?
                 AND quality_status IN ('complete','ok','partial')
                 {source_sql}
             ) SELECT * FROM ranked WHERE rn=1 ORDER BY day,scope_id""",
        tuple(params),
    ).fetchall()
    result = []
    for row in rows:
        try:
            observation_day = date.fromisoformat(str(row["day"]))
            if str(row["currency"]) == "CHF":
                value = Decimal(str(row["value_original"]))
            elif row["fx_rate_to_base"] is not None:
                value = Decimal(str(row["value_original"])) * Decimal(
                    str(row["fx_rate_to_base"])
                )
            else:
                continue
        except Exception:
            continue
        result.append(
            {
                "account_id": str(row["scope_id"]),
                "date": observation_day.isoformat(),
                "value": value,
                "source": str(row["source"]),
                "captured_at": str(row["captured_at"]),
            }
        )
    return result


def _official_rows(
    conn: Connection,
    *,
    account_ids: list[str],
    through: str,
    allowed_sources: tuple[str, ...],
) -> list[dict[str, Any]]:
    if not account_ids:
        return []
    placeholders = ",".join("?" for _ in account_ids)
    source_placeholders = ",".join("?" for _ in allowed_sources)
    rows = conn.execute(
        f"""WITH ranked AS (
              SELECT account_id,valuation_date,total_value_chf,source_type,
                     COALESCE(valuation_at,created_at) captured_at,
                     ROW_NUMBER() OVER(PARTITION BY account_id,valuation_date
                       ORDER BY COALESCE(valuation_at,created_at) DESC,snapshot_id DESC) rn
                FROM account_value_snapshots
               WHERE account_id IN ({placeholders}) AND valuation_date<=?
                 AND COALESCE(is_active,1)=1 AND updated_at IS NULL
                 AND source_type IN ({source_placeholders})
                 AND quality_status IN ('ok','complete')
             ) SELECT * FROM ranked WHERE rn=1 ORDER BY valuation_date,account_id""",
        (*account_ids, through, *allowed_sources),
    ).fetchall()
    result = []
    for row in rows:
        try:
            observation_day = date.fromisoformat(str(row["valuation_date"]))
            value = Decimal(str(row["total_value_chf"]))
        except Exception:
            continue
        if not value.is_finite() or value < Decimal("0"):
            continue
        result.append({
            "account_id": str(row["account_id"]),
            "date": observation_day.isoformat(),
            "value": value,
            "source": str(row["source_type"]),
            "captured_at": str(row["captured_at"]),
        })
    return result


def _postfinance_events(conn: Connection, *, through: str) -> tuple[dict[str, dict[str, Any]], list[dict[str, Any]]]:
    depot_ids = _role_account_ids(conn, "postfinance_etrading_depot")
    cash_ids = _role_account_ids(conn, "postfinance_etrading_cash")
    official = _official_rows(
        conn,
        account_ids=depot_ids,
        through=through,
        allowed_sources=("postfinance_official_import",),
    )
    model_rows = _latest_model_rows(
        conn,
        account_ids=depot_ids + cash_ids,
        through=through,
        source_prefix="daily_market_fx_v",
    )
    model_by_day: dict[str, dict[str, Decimal]] = {}
    for row in model_rows:
        model_by_day.setdefault(row["date"], {})[row["account_id"]] = row["value"]
    models: dict[str, Decimal] = {}
    model_observations: dict[str, dict[str, Any]] = {}
    for day, values in model_by_day.items():
        if depot_ids and cash_ids and all(account in values for account in depot_ids + cash_ids):
            models[day] = sum(values.values(), Decimal("0"))
            model_observations[day] = {
                "value": models[day],
                "captured_at": max(
                    row["captured_at"] for row in model_rows if row["date"] == day
                ),
            }
    events = {
        day: {"value": value, "quality": "modelled", "source_date": day}
        for day, value in models.items()
    }
    for row in official:
        events[row["date"]] = {
            "value": row["value"],
            "quality": "confirmed",
            "source_date": row["date"],
        }
    markers = _correction_markers(
        source_key="postfinance", official=official, models=model_observations
    )
    return events, markers


def _truewealth_events(conn: Connection, *, through: str) -> tuple[dict[str, dict[str, Any]], list[dict[str, Any]]]:
    account_ids = _role_account_ids(conn, "canonical_truewealth_total_value")
    official = _official_rows(
        conn,
        account_ids=account_ids,
        through=through,
        allowed_sources=("truewealth_official_import", "manual_total_value"),
    )
    model_rows = _latest_model_rows(
        conn,
        account_ids=account_ids,
        through=through,
        source="truewealth_modelled_daily",
    )
    models: dict[str, Decimal] = {}
    model_observations: dict[str, dict[str, Any]] = {}
    for row in model_rows:
        models[row["date"]] = models.get(row["date"], Decimal("0")) + row["value"]
        model_observations[row["date"]] = {
            "value": models[row["date"]],
            "captured_at": max(
                str(row["captured_at"]),
                str(model_observations.get(row["date"], {}).get("captured_at", "")),
            ),
        }
    events = {
        day: {"value": value, "quality": "modelled", "source_date": day}
        for day, value in models.items()
    }
    for row in official:
        events[row["date"]] = {
            "value": row["value"],
            "quality": "confirmed",
            "source_date": row["date"],
        }
    return events, _correction_markers(
        source_key="truewealth", official=official, models=model_observations
    )


def _crypto_events(conn: Connection, *, through: str) -> dict[str, dict[str, Any]]:
    account_ids = _role_account_ids(conn, "crypto_portfolio")
    if not account_ids:
        account_ids = [
            str(row[0])
            for row in conn.execute(
                """SELECT DISTINCT scope_id FROM portfolio_valuation_snapshots
                    WHERE scope_kind='account' AND source IN (?,?)
                      AND substr(valuation_at,1,10)<=?""",
                (
                    CRYPTO_VALUATION_SOURCE,
                    LEGACY_CRYPTO_VALUATION_SOURCE,
                    through,
                ),
            ).fetchall()
        ]
    rows = []
    for source in (CRYPTO_VALUATION_SOURCE, LEGACY_CRYPTO_VALUATION_SOURCE):
        rows.extend(
            _latest_model_rows(
                conn,
                account_ids=account_ids,
                through=through,
                source=source,
            )
        )
    latest_by_account_day: dict[tuple[str, str], dict[str, Any]] = {}
    for row in rows:
        key = (row["account_id"], row["date"])
        if key not in latest_by_account_day or str(row["captured_at"]) > str(
            latest_by_account_day[key]["captured_at"]
        ):
            latest_by_account_day[key] = row
    values: dict[str, Decimal] = {}
    for row in latest_by_account_day.values():
        values[row["date"]] = values.get(row["date"], Decimal("0")) + row["value"]
    return {
        day: {"value": value, "quality": "modelled", "source_date": day}
        for day, value in values.items()
    }


def _other_asset_events(conn: Connection, *, through: str) -> dict[str, dict[str, Any]]:
    """Aggregate confirmed non-cash account values without inventing daily precision."""
    rows = conn.execute(
        """WITH ranked AS (
             SELECT s.account_id,s.valuation_date,s.total_value_chf,
                    ROW_NUMBER() OVER(
                      PARTITION BY s.account_id,s.valuation_date
                      ORDER BY COALESCE(s.valuation_at,s.created_at) DESC,s.snapshot_id DESC
                    ) rn
               FROM account_value_snapshots s
               JOIN accounts a ON a.account_id=s.account_id
              WHERE a.is_active=1 AND a.account_type IN ('other_asset','membership')
                AND s.valuation_date<=? AND COALESCE(s.is_active,1)=1
                AND s.quality_status IN ('confirmed','ok','complete')
           ) SELECT account_id,valuation_date,total_value_chf
               FROM ranked WHERE rn=1 ORDER BY valuation_date,account_id""",
        (through,),
    ).fetchall()
    latest: dict[str, Decimal] = {}
    events: dict[str, dict[str, Any]] = {}
    for row in rows:
        try:
            value = Decimal(str(row["total_value_chf"]))
        except Exception:
            continue
        if not value.is_finite() or value < Decimal("0"):
            continue
        latest[str(row["account_id"])] = value
        day = str(row["valuation_date"])
        events[day] = {
            "value": sum(latest.values(), Decimal("0")),
            "quality": "confirmed",
            "source_date": day,
        }
    return events


def _manual_cash_correction_markers(conn: Connection, *, through: str) -> list[dict[str, str]]:
    rows = conn.execute(
        """SELECT snapshot_id,account_id,balance_date,amount_chf,created_at
             FROM cash_account_snapshots
            WHERE source='manual_screenshot_snapshot' AND balance_date<=?
            ORDER BY balance_date,created_at,snapshot_id""",
        (through,),
    ).fetchall()
    grouped: dict[str, dict[str, Decimal]] = {}
    for row in rows:
        previous = conn.execute(
            """SELECT amount_chf FROM cash_account_snapshots
                 WHERE account_id=? AND (
                   balance_date<? OR (balance_date=? AND (created_at<? OR (created_at=? AND snapshot_id<?)))
                 )
                 ORDER BY balance_date DESC,created_at DESC,snapshot_id DESC LIMIT 1""",
            (
                row["account_id"], row["balance_date"], row["balance_date"],
                row["created_at"], row["created_at"], row["snapshot_id"],
            ),
        ).fetchone()
        day = str(row["balance_date"])
        values = grouped.setdefault(day, {"confirmed": Decimal("0"), "previous": Decimal("0")})
        values["confirmed"] += Decimal(str(row["amount_chf"]))
        values["previous"] += Decimal(str(previous["amount_chf"])) if previous else Decimal("0")
    return [
        {
            "date": day,
            "source_key": "bank_cash",
            "confirmed_value_chf": _money(values["confirmed"]) or "0.00",
            "predecessor_model_value_chf": _money(values["previous"]) or "0.00",
            "difference_chf": _money(values["confirmed"] - values["previous"]) or "0.00",
        }
        for day, values in sorted(grouped.items())
    ]


def _correction_markers(
    *,
    source_key: str,
    official: list[dict[str, Any]],
    models: dict[str, dict[str, Any]],
) -> list[dict[str, str]]:
    markers = []
    model_days = sorted(models)
    for row in official:
        predecessor_days = [
            day
            for day in model_days
            if day < row["date"]
            or (
                day == row["date"]
                and str(models[day]["captured_at"]) < str(row["captured_at"])
            )
        ]
        if not predecessor_days:
            continue
        predecessor_day = predecessor_days[-1]
        predecessor = models[predecessor_day]["value"]
        markers.append(
            {
                "date": row["date"],
                "source_key": source_key,
                "confirmed_value_chf": _money(row["value"]) or "0.00",
                "predecessor_model_value_chf": _money(predecessor) or "0.00",
                "difference_chf": _money(row["value"] - predecessor) or "0.00",
            }
        )
    return markers


def _event_at_or_before(
    events: dict[str, dict[str, Any]], day: str
) -> dict[str, Any] | None:
    eligible = [event_day for event_day in events if event_day <= day]
    if not eligible:
        return None
    source_day = max(eligible)
    event = events[source_day]
    return {
        "value": event["value"],
        "quality": event["quality"] if source_day == day else "carried",
        "source_date": source_day,
    }


def _bank_accounts(conn: Connection) -> list[dict[str, str]]:
    rows = conn.execute(
        """SELECT a.account_id,a.account_name,COALESCE(psc.classification_role,'') role
             FROM accounts a
             LEFT JOIN performance_scope_classifications psc ON psc.account_id=a.account_id
            WHERE a.is_active=1 AND a.account_type='cash'
              AND (
                  EXISTS(SELECT 1 FROM cash_account_snapshots s WHERE s.account_id=a.account_id)
                  OR EXISTS(SELECT 1 FROM transactions t WHERE t.account_id=a.account_id)
                  OR EXISTS(SELECT 1 FROM cash_balances b WHERE b.account_id=a.account_id)
                  OR EXISTS(
                      SELECT 1 FROM budget_accounts ba
                      WHERE ba.linked_account_id=a.account_id AND ba.is_active=1
                  )
                  OR EXISTS(
                      SELECT 1 FROM household_account_source_mappings m
                      WHERE m.canonical_account_id=a.account_id AND m.is_active=1
                  )
              )
            ORDER BY a.account_name,a.account_id"""
    ).fetchall()
    excluded_roles = {"postfinance_etrading_cash", "postfinance_efinance_control"}
    return [
        {"account_id": str(row["account_id"]), "label": str(row["account_name"])}
        for row in rows
        if str(row["role"] or "") not in excluded_roles
    ]


def _safe_bank_label(label: str) -> str:
    """Expose only a generic label and an already-masked four-digit suffix."""

    suffix = re.search(r"(?:•{4}|\*{4}|x{4})\s*(\d{4})\b", label, re.IGNORECASE)
    return f"Bankkonto •••• {suffix.group(1)}" if suffix else "Bankkonto"


def _earliest_evidence(conn: Connection, *, fallback: date) -> date:
    rows = conn.execute(
        """SELECT day FROM (
             SELECT valuation_date day FROM account_value_snapshots
              WHERE COALESCE(is_active,1)=1 AND updated_at IS NULL
             UNION ALL SELECT substr(valuation_at,1,10) FROM portfolio_valuation_snapshots
             UNION ALL SELECT balance_date FROM cash_account_snapshots
           )"""
    ).fetchall()
    valid_days: list[date] = []
    for row in rows:
        try:
            valid_days.append(date.fromisoformat(str(row[0])))
        except (TypeError, ValueError):
            continue
    return min(valid_days, default=fallback)


def _period_start(
    conn: Connection, *, period: str, as_of: date, latest_anchor: date | None
) -> date:
    if period == "since_anchor":
        return latest_anchor or as_of
    if period == "1m":
        return _subtract_months(as_of, 1)
    if period == "3m":
        return _subtract_months(as_of, 3)
    if period == "ytd":
        return date(as_of.year, 1, 1)
    if period == "1y":
        return _subtract_months(as_of, 12)
    if period == "all":
        return _earliest_evidence(conn, fallback=as_of)
    raise ValueError("period must be since_anchor, 1m, 3m, ytd, 1y or all")


def build_modelled_wealth_development(
    conn: Connection, *, period: str = "1m", as_of: str | None = None
) -> dict[str, Any]:
    """Compose existing immutable valuation/snapshot sources into one read model."""

    if period not in MODEL_PERIODS:
        raise ValueError("period must be since_anchor, 1m, 3m, ytd, 1y or all")
    reference = date.fromisoformat(as_of) if as_of else date.today()
    through = reference.isoformat()
    postfinance, pf_markers = _postfinance_events(conn, through=through)
    truewealth, tw_markers = _truewealth_events(conn, through=through)
    crypto = _crypto_events(conn, through=through)
    other_assets = _other_asset_events(conn, through=through)
    investment_events = {
        "postfinance": postfinance,
        "truewealth": truewealth,
        "crypto": crypto,
        "other_assets": other_assets,
    }
    expected_investment = {
        "postfinance": bool(
            _role_account_ids(conn, "postfinance_etrading_depot")
            or _role_account_ids(conn, "postfinance_etrading_cash")
        ),
        "truewealth": bool(
            _role_account_ids(conn, "canonical_truewealth_total_value")
        ),
        "crypto": bool(_role_account_ids(conn, "crypto_portfolio")),
        "other_assets": bool(other_assets),
    }
    # Household anchors come from confirmed portfolio-import anchors. Component-only
    # cash/membership corrections remain event markers and must not move the solid-line
    # boundary or make mixed-date values look fully confirmed.
    confirmed_days: list[date] = []
    for events in (postfinance, truewealth):
        for day, event in events.items():
            if event["quality"] != "confirmed":
                continue
            try:
                confirmed_days.append(date.fromisoformat(day))
            except ValueError:
                continue
    latest_anchor = max(confirmed_days, default=None)
    start = _period_start(
        conn, period=period, as_of=reference, latest_anchor=latest_anchor
    )
    if start > reference:
        start = reference
    if (reference - start).days > 5000:
        start = reference - timedelta(days=5000)

    bank_accounts = _bank_accounts(conn)
    points: list[dict[str, Any]] = []
    unknown_identity_by_day: dict[str, frozenset[str]] = {}
    event_dates = {
        day
        for events in investment_events.values()
        for day in events
        if start.isoformat() <= day <= through
    }
    event_dates.update(
        str(row[0])
        for row in conn.execute(
            "SELECT DISTINCT balance_date FROM cash_account_snapshots WHERE balance_date BETWEEN ? AND ?",
            (start.isoformat(), through),
        ).fetchall()
    )
    for account in bank_accounts:
        movement_evidence = _authoritative_cash_movements(
            conn,
            account_id=account["account_id"],
            after=start.isoformat(),
            through=through,
        )
        event_dates.update(movement_evidence["days"])
    cursor = start
    while cursor <= reference:
        day = cursor.isoformat()
        components: list[dict[str, Any]] = []
        qualities: list[str] = []
        missing_investment: list[str] = []
        known_total = Decimal("0")
        for key in ("postfinance", "truewealth", "crypto", "other_assets"):
            selected = _event_at_or_before(investment_events[key], day)
            value = selected["value"] if selected else None
            quality = selected["quality"] if selected else "unavailable"
            if value is not None:
                known_total += value
                qualities.append(quality)
            elif expected_investment[key]:
                missing_investment.append(key)
            components.append(
                {
                    "key": key,
                    "label": COMPONENT_LABELS[key],
                    "value_chf": _money(value),
                    "quality": quality,
                    "source_date": selected["source_date"] if selected else None,
                }
            )

        bank_total = Decimal("0")
        bank_qualities: list[str] = []
        unknown_on_day: list[str] = []
        bank_source_days: list[str] = []
        for account in bank_accounts:
            evidence = effective_cash_evidence(
                conn, account_id=account["account_id"], as_of=day
            )
            if evidence["value_chf"] is None:
                unknown_on_day.append(account["account_id"])
                continue
            bank_total += Decimal(evidence["value_chf"])
            bank_qualities.append(str(evidence["quality"]))
            if evidence["source_date"]:
                bank_source_days.append(str(evidence["source_date"]))
        if bank_qualities:
            bank_quality = (
                "modelled"
                if "modelled" in bank_qualities
                else "carried"
                if "carried" in bank_qualities
                else "confirmed"
            )
            known_total += bank_total
            qualities.append(bank_quality)
            bank_value = _money(bank_total)
        else:
            bank_quality = "unavailable"
            bank_value = None
        components.append(
            {
                "key": "bank_cash",
                "label": COMPONENT_LABELS["bank_cash"],
                "value_chf": bank_value,
                "quality": bank_quality,
                "source_date": min(bank_source_days, default=None),
            }
        )
        if not qualities:
            cursor += timedelta(days=1)
            continue
        has_confirmed_anchor = any(
            component["quality"] == "confirmed" and component["source_date"] == day
            for component in components
        )
        has_modelled_value = (latest_anchor is None or cursor > latest_anchor) and any(
            component["quality"] == "modelled" and component["source_date"] == day
            for component in components
        )
        point_quality = (
            "incomplete"
            if unknown_on_day or missing_investment
            else "modelled"
            if "modelled" in qualities and (latest_anchor is None or cursor > latest_anchor)
            else "carried"
            if "carried" in qualities
            else "confirmed"
        )
        points.append(
            {
                "date": day,
                "value_chf": _money(known_total) or "0.00",
                "quality": point_quality,
                "has_confirmed_anchor": has_confirmed_anchor,
                "has_modelled_value": has_modelled_value,
                "components": components,
                "excluded_account_count": len(unknown_on_day)
                + len(missing_investment),
            }
        )
        unknown_identity_by_day[day] = frozenset(
            [f"bank:{account_id}" for account_id in unknown_on_day]
            + [f"component:{key}" for key in missing_investment]
        )
        cursor += timedelta(days=1)

    current_point = points[-1] if points else None
    current_unknown = []
    for index, account in enumerate(bank_accounts, start=1):
        evidence = effective_cash_evidence(
            conn, account_id=account["account_id"], as_of=through
        )
        if evidence["value_chf"] is None:
            current_unknown.append(
                {
                    "key": f"unknown-bank-{index}",
                    "label": _safe_bank_label(account["label"]),
                    "reason_code": "confirmed_cash_evidence_missing",
                }
            )
    for key in ("postfinance", "truewealth", "crypto", "other_assets"):
        if expected_investment[key] and not _event_at_or_before(
            investment_events[key], through
        ):
            current_unknown.append(
                {
                    "key": f"unknown-component-{key}",
                    "label": COMPONENT_LABELS[key],
                    "reason_code": "stored_valuation_evidence_missing",
                }
            )

    anchor_point = None
    if latest_anchor:
        anchor_point = next(
            (point for point in points if point["date"] == latest_anchor.isoformat()),
            None,
        )
    anchor = (
        {
            "date": anchor_point["date"],
            "value_chf": anchor_point["value_chf"],
            "quality": anchor_point["quality"],
        }
        if anchor_point
        else None
    )
    comparable_baseline = points[0] if points else None
    if current_point:
        current_unknown_identity = unknown_identity_by_day.get(
            str(current_point["date"]), frozenset()
        )
        current_known_keys = {
            str(item["key"])
            for item in current_point["components"]
            if item["value_chf"] is not None
        }
        comparable_baseline = next(
            (
                point
                for point in points
                if {
                    str(item["key"])
                    for item in point["components"]
                    if item["value_chf"] is not None
                }
                >= current_known_keys
                and unknown_identity_by_day.get(
                    str(point["date"]), frozenset()
                )
                == current_unknown_identity
            ),
            comparable_baseline,
        )
    baseline_point = (
        anchor_point
        if period == "since_anchor" and anchor_point is not None
        else comparable_baseline
    )
    baseline = (
        {
            "date": baseline_point["date"],
            "value_chf": baseline_point["value_chf"],
            "quality": baseline_point["quality"],
        }
        if baseline_point
        else None
    )
    current = (
        {
            "date": current_point["date"],
            "value_chf": current_point["value_chf"],
            "quality": current_point["quality"],
        }
        if current_point
        else None
    )
    baseline_value = Decimal(baseline["value_chf"]) if baseline else None
    change = (
        Decimal(current["value_chf"]) - baseline_value
        if current and baseline_value is not None
        else None
    )
    change_pct = None
    if (
        change is not None
        and baseline_value is not None
        and baseline_value != Decimal("0")
    ):
        change_pct = change / baseline_value * Decimal("100")
    chart_visible = len(event_dates) >= 2 and len(points) >= 2
    component_summaries: list[dict[str, Any]] = []
    if current_point:
        baseline_components = {
            str(item["key"]): item for item in (baseline_point or {}).get("components", [])
        }
        for item in current_point["components"]:
            key = str(item["key"])
            opening = baseline_components.get(key)
            current_value = (
                Decimal(str(item["value_chf"]))
                if item["value_chf"] is not None
                else None
            )
            opening_value = (
                Decimal(str(opening["value_chf"]))
                if opening and opening["value_chf"] is not None
                else None
            )
            component_change = (
                current_value - opening_value
                if current_value is not None and opening_value is not None
                else None
            )
            component_change_pct = (
                component_change / opening_value * Decimal("100")
                if component_change is not None
                and opening_value is not None
                and opening_value != Decimal("0")
                else None
            )
            quality = str(item["quality"])
            if key == "bank_cash" and current_unknown:
                quality = "incomplete"
            component_summaries.append(
                {
                    "key": key,
                    "label": str(item["label"]),
                    "current_value_chf": _money(current_value),
                    "change_chf": _money(component_change),
                    "change_pct": format(component_change_pct.quantize(PERCENT), "f")
                    if component_change_pct is not None
                    else None,
                    "quality": quality,
                    "as_of": item["source_date"],
                    "unknown_account_count": sum(
                        1
                        for unknown in current_unknown
                        if (
                            str(unknown["key"]).startswith("unknown-bank-")
                            if key == "bank_cash"
                            else unknown["key"] == f"unknown-component-{key}"
                        )
                    ),
                }
            )
    return {
        "status": "available" if points else "unavailable",
        "period": {
            "preset": period,
            "from": start.isoformat(),
            "to": through,
        },
        "last_confirmed_anchor_date": latest_anchor.isoformat() if latest_anchor else None,
        "anchor": anchor,
        "baseline": baseline,
        "current": current,
        "change_chf": _money(change),
        "change_pct": format(change_pct.quantize(PERCENT), "f")
        if change_pct is not None
        else None,
        "chart_visible": chart_visible,
        "points": points,
        "components": component_summaries,
        "correction_markers": sorted(
            [
                marker
                for marker in pf_markers + tw_markers + _manual_cash_correction_markers(conn, through=through)
                if start.isoformat() <= marker["date"] <= through
            ],
            key=lambda item: (item["date"], item["source_key"]),
        ),
        "unknown_accounts": current_unknown,
        "method": "modelled_wealth_daily_v1",
        "disclaimer": "Geschätzte Entwicklung aus bestätigten Ankern, gespeicherten Tagesbewertungen und fortgeschriebenen bekannten Salden; keine verifizierte TTWROR oder XIRR.",
    }

__HERMES_CWD_8d46a20096ed__/home/agent/.hermes/worktrees/FinanceManager-sprint23.3-hotfix__HERMES_CWD_8d46a20096ed__
