"""Plano de Contas padrão (raiz › grupo › conta), numerado no estilo do APP32.

Cada conta tem uma `chave` estável (o código antigo, p.ex. `PC-PESSOAS-SALARIOS`): o sistema acha as contas fixas
(folha do Funcionário, Máquina, Ambiente) por ela, então renumerar ou mover contas na tela não quebra nada.
"""

from dataclasses import dataclass

from sqlalchemy.orm import Session

from custos_precificacao import arvore
from custos_precificacao.models import PlanoDeContas


@dataclass(frozen=True)
class ContaPadrao:
    chave: str
    codigo: str
    nome: str
    tipo: str
    pai: str | None  # chave do pai
    analitica: bool = True  # False = sintética (tem filhos)


def _c(chave, codigo, nome, tipo, pai, analitica=True):
    return ContaPadrao(chave, codigo, nome, tipo, pai, analitica)


PLANO_PADRAO: tuple[ContaPadrao, ...] = (
    _c("PC-RECEITAS", "1", "Receitas", "receita", None, False),
    _c("PC-RECEITA-VENDAS", "1.1", "Receita de Vendas", "receita", "PC-RECEITAS"),
    _c("PC-CUSTOS", "2", "Custos", "custo", None, False),
    _c("PC-CUSTOS-PESSOAS", "2.1", "Pessoas", "custo", "PC-CUSTOS", False),
    _c("PC-PESSOAS-SALARIOS", "2.1.01", "Salários e Ordenados", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-PESSOAS-OUTREM", "2.1.02", "Outras Remunerações", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-PESSOAS-ALIM", "2.1.03", "Alimentação", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-PESSOAS-TRANSP", "2.1.04", "Transporte", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-PESSOAS-ASSMED", "2.1.05", "Assistência Médica", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-PESSOAS-FGTS", "2.1.06", "FGTS", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-PESSOAS-INSS", "2.1.07", "INSS", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-PESSOAS-PROVIS", "2.1.08", "Provisões sobre Folha", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-PESSOAS-VTVA", "2.1.09", "Transporte e Alimentação", "custo", "PC-CUSTOS-PESSOAS"),
    _c("PC-CUSTOS-MAQEQUIP", "2.2", "Máquinas/Equipamentos", "custo", "PC-CUSTOS", False),
    _c("PC-MAQEQ-DEPREC", "2.2.01", "Depreciação", "custo", "PC-CUSTOS-MAQEQUIP"),
    _c("PC-MAQEQ-MANUT", "2.2.02", "Manutenção", "custo", "PC-CUSTOS-MAQEQUIP"),
    _c("PC-MAQEQ-ENERGIA", "2.2.03", "Energia", "custo", "PC-CUSTOS-MAQEQUIP"),
    _c("PC-MAQEQ-SEGURO", "2.2.04", "Seguro", "custo", "PC-CUSTOS-MAQEQUIP"),
    _c("PC-MAQEQ-OUTROS", "2.2.05", "Outros Custos de Máquina", "custo", "PC-CUSTOS-MAQEQUIP"),
    _c("PC-CUSTOS-INSTAL", "2.3", "Instalações", "custo", "PC-CUSTOS", False),
    _c("PC-INSTAL-ALUGUEL", "2.3.01", "Aluguel", "custo", "PC-CUSTOS-INSTAL"),
    _c("PC-INSTAL-ENERGIA", "2.3.02", "Energia", "custo", "PC-CUSTOS-INSTAL"),
    _c("PC-INSTAL-MANUT", "2.3.03", "Manutenção Predial", "custo", "PC-CUSTOS-INSTAL"),
    _c("PC-INSTAL-AGUA", "2.3.04", "Água", "custo", "PC-CUSTOS-INSTAL"),
    _c("PC-INSTAL-IPTU", "2.3.05", "IPTU e Condomínio", "custo", "PC-CUSTOS-INSTAL"),
    _c("PC-INSTAL-LIMPSEG", "2.3.06", "Limpeza e Segurança", "custo", "PC-CUSTOS-INSTAL"),
    _c("PC-INSTAL-DEPREC", "2.3.07", "Depreciação", "custo", "PC-CUSTOS-INSTAL"),
    _c("PC-CUSTOS-INSUMOS", "2.4", "Insumos", "custo", "PC-CUSTOS"),
    _c("PC-CUSTOS-OUTROS", "2.5", "Outros Custos", "custo", "PC-CUSTOS"),
    _c("PC-DESPESAS", "3", "Despesas", "despesa", None, False),
    _c("PC-DESP-COMERCIAIS", "3.1", "Despesas Comerciais", "despesa", "PC-DESPESAS"),
    _c("PC-DESP-FINANCEIRAS", "3.2", "Despesas Financeiras", "despesa", "PC-DESPESAS"),
    _c("PC-DESP-TRIBUTARIAS", "3.3", "Despesas Tributárias", "despesa", "PC-DESPESAS"),
    _c("PC-DESP-OUTRAS", "3.4", "Outras Despesas", "despesa", "PC-DESPESAS"),
    _c("PC-IMPOSTOS", "4", "Impostos", "imposto", None, False),
    _c("PC-IMP-VENDAS", "4.1", "Impostos Incidentes sobre Vendas", "imposto", "PC-IMPOSTOS", False),
    _c("PC-IMP-IPI", "4.1.01", "IPI", "imposto", "PC-IMP-VENDAS"),
    _c("PC-IMP-ICMSST", "4.1.02", "ICMS-ST", "imposto", "PC-IMP-VENDAS"),
    _c("PC-IMP-ICMS", "4.1.03", "ICMS", "imposto", "PC-IMP-VENDAS"),
    _c("PC-IMP-PIS", "4.1.04", "PIS", "imposto", "PC-IMP-VENDAS"),
    _c("PC-IMP-COFINS", "4.1.05", "COFINS", "imposto", "PC-IMP-VENDAS"),
    _c("PC-IMP-CBS", "4.1.06", "CBS", "imposto", "PC-IMP-VENDAS"),
    _c("PC-IMP-IRCSLL", "4.2", "IR/CSLL", "imposto", "PC-IMPOSTOS"),
)
POR_CHAVE: dict[str, ContaPadrao] = {c.chave: c for c in PLANO_PADRAO}


def conta_por_chave(session: Session, chave: str) -> PlanoDeContas | None:
    return session.query(PlanoDeContas).filter(PlanoDeContas.chave == chave).one_or_none()


def garantir_conta(session: Session, chave: str) -> tuple[PlanoDeContas, int]:
    """Devolve a conta da `chave`, criando-a (e os ancestrais que faltarem) no lugar padrão. `(conta, quantas criou)`."""
    existente = conta_por_chave(session, chave)
    if existente is not None:
        return existente, 0
    padrao = POR_CHAVE[chave]
    criadas = 0
    pai = None
    if padrao.pai is not None:
        pai, criadas = garantir_conta(session, padrao.pai)
    sufixo = padrao.codigo.rsplit(".", 1)[-1]
    codigo = arvore.montar_codigo(pai, sufixo)
    ocupado = session.query(PlanoDeContas).filter(PlanoDeContas.codigo == codigo).first() is not None
    conta = PlanoDeContas(
        chave=chave, nome=padrao.nome, tipo=padrao.tipo, aceita_lancamento=padrao.analitica,
        parent_id=pai.id if pai else None, codigo="", sufixo=None,
    )
    if ocupado:  # o lugar padrão foi tomado por outra conta: usa o próximo sufixo livre do pai
        sufixo = arvore.proximo_sufixo(session, PlanoDeContas, pai)
        codigo = arvore.montar_codigo(pai, sufixo)
    conta.codigo, conta.sufixo = codigo, sufixo
    session.add(conta)
    if pai is not None and pai.aceita_lancamento and not arvore.total_de_usos(session, pai):
        pai.aceita_lancamento = False
    session.flush()
    return conta, criadas + 1
