"""add posting ledger, drop month_entry.applied

Revision ID: cc60e7f73a1c
Revises: a4ec4f8f6e9f
Create Date: 2026-04-17 17:25:53.487094

Creates the posting table, seeds one "opening balance" posting per
existing month_entry whose applied value is non-zero, and drops the
applied column from month_entry. After this revision applied is
computed as SUM(posting.amount) over the entry's postings.
"""
from datetime import datetime, timezone
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


revision: str = "cc60e7f73a1c"
down_revision: Union[str, Sequence[str], None] = "a4ec4f8f6e9f"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # 1. Create posting table
    op.create_table(
        "posting",
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("month_entry_id", sa.Integer(), nullable=False),
        sa.Column("occurred_on", sa.Date(), nullable=False),
        sa.Column("amount", sa.Numeric(precision=10, scale=2), nullable=False),
        sa.Column("description", sa.String(length=256), nullable=True),
        sa.Column("payee", sa.String(length=128), nullable=True),
        sa.Column(
            "created_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("(CURRENT_TIMESTAMP)"),
            nullable=False,
        ),
        sa.Column(
            "updated_at",
            sa.DateTime(timezone=True),
            server_default=sa.text("(CURRENT_TIMESTAMP)"),
            nullable=False,
        ),
        sa.ForeignKeyConstraint(
            ["month_entry_id"], ["month_entry.id"], ondelete="CASCADE"
        ),
        sa.PrimaryKeyConstraint("id"),
    )
    with op.batch_alter_table("posting", schema=None) as batch_op:
        batch_op.create_index(
            batch_op.f("ix_posting_month_entry_id"),
            ["month_entry_id"],
            unique=False,
        )

    # 2. Seed opening-balance postings from the existing month_entry.applied
    conn = op.get_bind()
    rows = conn.execute(
        sa.text(
            "SELECT me.id, me.applied, m.activated_at, m.created_at "
            "FROM month_entry me JOIN month m ON m.id = me.month_id "
            "WHERE me.applied IS NOT NULL AND me.applied != 0"
        )
    ).fetchall()
    now = datetime.now(timezone.utc).replace(tzinfo=None)
    for row in rows:
        me_id, applied, activated_at, created_at = row
        occurred = activated_at or created_at
        # Truncate the timestamp to a date for the DATE column
        if hasattr(occurred, "date"):
            occurred = occurred.date()
        if isinstance(occurred, str):
            # SQLite may give back an ISO string; take the date portion
            occurred = occurred[:10]
        conn.execute(
            sa.text(
                "INSERT INTO posting "
                "(month_entry_id, occurred_on, amount, description, created_at, updated_at) "
                "VALUES (:me_id, :occurred, :amount, :desc, :now, :now)"
            ),
            {
                "me_id": me_id,
                "occurred": occurred,
                "amount": applied,
                "desc": "opening balance",
                "now": now,
            },
        )

    # 3. Drop the applied column from month_entry
    with op.batch_alter_table("month_entry", schema=None) as batch_op:
        batch_op.drop_column("applied")


def downgrade() -> None:
    # Add the applied column back with a default of 0.00 for NOT NULL
    with op.batch_alter_table("month_entry", schema=None) as batch_op:
        batch_op.add_column(
            sa.Column(
                "applied",
                sa.NUMERIC(precision=10, scale=2),
                server_default=sa.text("'0.00'"),
                nullable=False,
            )
        )

    # Restore applied as SUM of postings
    conn = op.get_bind()
    conn.execute(
        sa.text(
            "UPDATE month_entry SET applied = ("
            "SELECT COALESCE(SUM(amount), 0) FROM posting "
            "WHERE posting.month_entry_id = month_entry.id"
            ")"
        )
    )

    with op.batch_alter_table("posting", schema=None) as batch_op:
        batch_op.drop_index(batch_op.f("ix_posting_month_entry_id"))
    op.drop_table("posting")
