"""
Modelos SQLAlchemy — todos os módulos do OkGestor.

IDs de Colaborador e Ferias mantêm UUID string para compatibilidade
com dados JSON existentes e facilidade de referência externa.
"""

import uuid
from datetime import date, datetime

from sqlalchemy import (
    Boolean, Date, DateTime, Float, ForeignKey,
    Integer, String, Text, UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship

from db.database import Base


def _novo_uuid() -> str:
    return str(uuid.uuid4())


# ══════════════════════════════════════════════════════════════════════════════
# MÓDULO: EQUIPE / RH
# ══════════════════════════════════════════════════════════════════════════════

class Colaborador(Base):
    __tablename__ = "colaboradores"

    id: Mapped[str]   = mapped_column(String(36), primary_key=True, default=_novo_uuid)
    nome:    Mapped[str]      = mapped_column(String(200), nullable=False)
    apelido: Mapped[str | None] = mapped_column(String(100))
    cpf:  Mapped[str | None] = mapped_column(String(14),  unique=True, nullable=True)
    cargo:    Mapped[str | None] = mapped_column(String(100))
    salario:  Mapped[float]      = mapped_column(Float, default=0.0)
    admissao: Mapped[date | None]= mapped_column(Date, nullable=True)
    telefone:  Mapped[str | None] = mapped_column(String(20))
    email:     Mapped[str | None] = mapped_column(String(200))
    status:    Mapped[str]        = mapped_column(String(10), default="ativo")
    adiantamento: Mapped[float]  = mapped_column(Float, default=0.0)
    obs:       Mapped[str | None] = mapped_column(Text)
    chave_pix: Mapped[str | None] = mapped_column(String(200))
    uniformes: Mapped[str | None] = mapped_column(Text)
    demissao:  Mapped[date | None]= mapped_column(Date, nullable=True)
    criado_em:     Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
    atualizado_em: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.now, onupdate=datetime.now
    )

    registros_ponto: Mapped[list["RegistroPonto"]] = relationship(
        back_populates="colaborador", cascade="all, delete-orphan"
    )
    escalas: Mapped[list["EscalaItem"]] = relationship(
        back_populates="colaborador", cascade="all, delete-orphan"
    )
    ferias: Mapped[list["Ferias"]] = relationship(
        back_populates="colaborador", cascade="all, delete-orphan"
    )

    def __repr__(self) -> str:
        return f"<Colaborador {self.nome} [{self.status}]>"

    def to_dict(self) -> dict:
        return {
            "id":              self.id,
            "nome":            self.nome,
            "apelido":         self.apelido or "",
            "nome_exibicao":   self.apelido or self.nome,
            "cpf":          self.cpf or "",
            "cargo":        self.cargo or "",
            "salario":      self.salario or 0.0,
            "adiantamento": self.adiantamento or 0.0,
            "admissao":     self.admissao.strftime("%d/%m/%Y") if self.admissao else "",
            "telefone":     self.telefone  or "",
            "email":        self.email     or "",
            "status":       self.status,
            "obs":          self.obs       or "",
            "chave_pix":    self.chave_pix or "",
            "uniformes":    self.uniformes or "",
            "demissao":     self.demissao.strftime("%d/%m/%Y") if self.demissao else "",
        }


class RegistroPonto(Base):
    __tablename__ = "registros_ponto"
    __table_args__ = (
        UniqueConstraint("colaborador_id", "data", name="uq_ponto_colab_data"),
    )

    id:    Mapped[int]  = mapped_column(Integer, primary_key=True, autoincrement=True)
    colaborador_id: Mapped[str] = mapped_column(
        String(36), ForeignKey("colaboradores.id", ondelete="CASCADE"), nullable=False
    )
    data:   Mapped[date] = mapped_column(Date, nullable=False)
    status: Mapped[str]  = mapped_column(String(3), nullable=False)
    obs:    Mapped[str | None] = mapped_column(Text)

    colaborador: Mapped["Colaborador"] = relationship(back_populates="registros_ponto")

    # Status válidos: P F FJ AT FO HE


class EscalaItem(Base):
    __tablename__ = "escala_itens"
    __table_args__ = (
        UniqueConstraint(
            "colaborador_id", "semana_inicio", "dia_semana",
            name="uq_escala_colab_semana_dia",
        ),
    )

    id:    Mapped[int]  = mapped_column(Integer, primary_key=True, autoincrement=True)
    colaborador_id: Mapped[str] = mapped_column(
        String(36), ForeignKey("colaboradores.id", ondelete="CASCADE"), nullable=False
    )
    semana_inicio: Mapped[date] = mapped_column(Date, nullable=False)  # sempre segunda-feira
    dia_semana:    Mapped[int]  = mapped_column(Integer, nullable=False)  # 0=Seg … 6=Dom
    turno:         Mapped[str]  = mapped_column(String(3), default="")    # T1 T2 T3 FO ""

    colaborador: Mapped["Colaborador"] = relationship(back_populates="escalas")


class Turno(Base):
    """Turnos configuráveis pelo usuário (ex: Manhã 08-17, Tarde 12-21)."""
    __tablename__ = "turnos"

    id:          Mapped[int]  = mapped_column(Integer, primary_key=True, autoincrement=True)
    nome:        Mapped[str]  = mapped_column(String(100), nullable=False)
    abreviacao:  Mapped[str]  = mapped_column(String(4),  nullable=False)   # "M", "T", "N"
    hora_inicio: Mapped[str]  = mapped_column(String(5),  default="08:00")  # "HH:MM"
    hora_fim:    Mapped[str]  = mapped_column(String(5),  default="17:00")
    cor:         Mapped[str]  = mapped_column(String(7),  default="#1A472A")
    ativo:       Mapped[bool] = mapped_column(Boolean,    default=True)

    def to_dict(self) -> dict:
        return {
            "id":          self.id,
            "nome":        self.nome,
            "abreviacao":  self.abreviacao,
            "hora_inicio": self.hora_inicio,
            "hora_fim":    self.hora_fim,
            "cor":         self.cor,
            "ativo":       self.ativo,
            "label":       f"{self.nome} {self.hora_inicio}–{self.hora_fim}",
        }


class Ferias(Base):
    __tablename__ = "ferias"

    id: Mapped[str]  = mapped_column(String(36), primary_key=True, default=_novo_uuid)
    colaborador_id: Mapped[str] = mapped_column(
        String(36), ForeignKey("colaboradores.id", ondelete="CASCADE"), nullable=False
    )
    inicio: Mapped[date] = mapped_column(Date, nullable=False)
    fim:    Mapped[date] = mapped_column(Date, nullable=False)
    obs:    Mapped[str | None] = mapped_column(Text)
    criado_em: Mapped[datetime] = mapped_column(DateTime, default=datetime.now)

    colaborador: Mapped["Colaborador"] = relationship(back_populates="ferias")

    def to_dict(self) -> dict:
        return {
            "id":         self.id,
            "id_colab":   self.colaborador_id,
            "nome_colab": self.colaborador.nome if self.colaborador else "",
            "inicio":     self.inicio.isoformat(),
            "fim":        self.fim.isoformat(),
            "obs":        self.obs or "",
        }


# ══════════════════════════════════════════════════════════════════════════════
# MÓDULO: CONFIGURAÇÕES DA EMPRESA
# ══════════════════════════════════════════════════════════════════════════════

class ConfigEmpresa(Base):
    """Registro único (id=1) com os dados da empresa para relatórios e impressões."""
    __tablename__ = "config_empresa"

    id: Mapped[int] = mapped_column(Integer, primary_key=True, default=1)

    # Identificação
    nome:             Mapped[str | None] = mapped_column(String(200))
    nome_fantasia:    Mapped[str | None] = mapped_column(String(200))
    cnpj:             Mapped[str | None] = mapped_column(String(18))
    ie:               Mapped[str | None] = mapped_column(String(30))
    im:               Mapped[str | None] = mapped_column(String(30))

    # Endereço
    logradouro:   Mapped[str | None] = mapped_column(String(300))
    numero:       Mapped[str | None] = mapped_column(String(10))
    complemento:  Mapped[str | None] = mapped_column(String(100))
    bairro:       Mapped[str | None] = mapped_column(String(100))
    cidade:       Mapped[str | None] = mapped_column(String(100))
    estado:       Mapped[str | None] = mapped_column(String(2))
    cep:          Mapped[str | None] = mapped_column(String(9))

    # Contato
    telefone: Mapped[str | None] = mapped_column(String(20))
    email:    Mapped[str | None] = mapped_column(String(200))
    site:     Mapped[str | None] = mapped_column(String(200))

    # Responsável
    responsavel:       Mapped[str | None] = mapped_column(String(200))
    cargo_responsavel: Mapped[str | None] = mapped_column(String(100))

    # Logotipo
    logo_path: Mapped[str | None] = mapped_column(String(500))

    # Padrões Fechamento de Caixa
    fc_conta_dinheiro: Mapped[str | None] = mapped_column(String(100))
    fc_conta_moedas:   Mapped[str | None] = mapped_column(String(100))

    atualizado_em: Mapped[datetime] = mapped_column(
        DateTime, default=datetime.now, onupdate=datetime.now
    )

    def to_dict(self) -> dict:
        return {
            "nome":             self.nome             or "",
            "nome_fantasia":    self.nome_fantasia    or "",
            "cnpj":             self.cnpj             or "",
            "ie":               self.ie               or "",
            "im":               self.im               or "",
            "logradouro":       self.logradouro       or "",
            "numero":           self.numero           or "",
            "complemento":      self.complemento      or "",
            "bairro":           self.bairro           or "",
            "cidade":           self.cidade           or "",
            "estado":           self.estado           or "",
            "cep":              self.cep              or "",
            "telefone":         self.telefone         or "",
            "email":            self.email            or "",
            "site":             self.site             or "",
            "responsavel":      self.responsavel      or "",
            "cargo_responsavel":self.cargo_responsavel or "",
            "logo_path":        self.logo_path        or "",
            "fc_conta_dinheiro": self.fc_conta_dinheiro or "",
            "fc_conta_moedas":   self.fc_conta_moedas   or "",
        }


# ══════════════════════════════════════════════════════════════════════════════
# MÓDULO: FINANCEIRO — CONTAS A PAGAR / RECEBER
# ══════════════════════════════════════════════════════════════════════════════

class LancamentoBaixa(Base):
    """Registro individual de pagamento parcial ou total de um título."""
    __tablename__ = "lancamentos_baixa"

    id:         Mapped[int]        = mapped_column(Integer, primary_key=True, autoincrement=True)
    titulo_id:  Mapped[int]        = mapped_column(
        Integer, ForeignKey("titulos.id", ondelete="CASCADE"), nullable=False
    )
    data:       Mapped[date]       = mapped_column(Date, nullable=False)
    hora:       Mapped[str]        = mapped_column(String(5), default="00:00")   # HH:MM
    historico:  Mapped[str | None] = mapped_column(String(200))
    conta_nome: Mapped[str | None] = mapped_column(String(100))
    cheque:     Mapped[str | None] = mapped_column(String(50))
    valor:      Mapped[float]      = mapped_column(Float, nullable=False, default=0.0)
    criado_em:  Mapped[datetime]   = mapped_column(DateTime, default=datetime.now)

    titulo: Mapped["Titulo"] = relationship(back_populates="lancamentos")

    def to_dict(self) -> dict:
        return {
            "id":         self.id,
            "titulo_id":  self.titulo_id,
            "data":       self.data.strftime("%d/%m/%Y") if self.data else "",
            "hora":       self.hora or "",
            "historico":  self.historico or "PAGAMENTO",
            "conta_nome": self.conta_nome or "",
            "cheque":     self.cheque or "",
            "valor":      self.valor or 0.0,
        }


class Titulo(Base):
    """Título financeiro — conta a pagar ou a receber."""
    __tablename__ = "titulos"

    id:             Mapped[int]        = mapped_column(Integer, primary_key=True, autoincrement=True)
    tipo:           Mapped[str]        = mapped_column(String(8),   nullable=False)   # pagar | receber
    descricao:      Mapped[str]        = mapped_column(String(300), nullable=False)
    categoria:      Mapped[str | None] = mapped_column(String(100))
    nome_contato:   Mapped[str | None] = mapped_column(String(200))
    valor:          Mapped[float]      = mapped_column(Float, default=0.0)
    vencimento:     Mapped[date]       = mapped_column(Date, nullable=False)
    data_emissao:   Mapped[date | None]= mapped_column(Date, nullable=True)
    status:         Mapped[str]        = mapped_column(String(12), default="aberto")  # aberto|pago|vencido|cancelado
    data_pagamento: Mapped[date | None]= mapped_column(Date, nullable=True)
    valor_pago:     Mapped[float | None] = mapped_column(Float, nullable=True)
    obs:            Mapped[str | None] = mapped_column(Text)
    # Campos de configuração financeira (adicionados via ALTER TABLE em criar_tabelas)
    centro_custo:    Mapped[str | None] = mapped_column(String(100))
    forma_pagamento: Mapped[str | None] = mapped_column(String(100))
    conta_nome:      Mapped[str | None] = mapped_column(String(100))
    criado_em:    Mapped[datetime] = mapped_column(DateTime, default=datetime.now)
    atualizado_em: Mapped[datetime] = mapped_column(DateTime, default=datetime.now, onupdate=datetime.now)

    lancamentos: Mapped[list["LancamentoBaixa"]] = relationship(
        back_populates="titulo", cascade="all, delete-orphan",
        order_by="LancamentoBaixa.data, LancamentoBaixa.hora",
    )

    def to_dict(self) -> dict:
        return {
            "id":              self.id,
            "tipo":            self.tipo,
            "descricao":       self.descricao,
            "categoria":       self.categoria       or "",
            "nome_contato":    self.nome_contato    or "",
            "valor":           self.valor           or 0.0,
            "vencimento":      self.vencimento.strftime("%d/%m/%Y") if self.vencimento else "",
            "data_emissao":    self.data_emissao.strftime("%d/%m/%Y") if self.data_emissao else "",
            "status":          self.status,
            "data_pagamento":  self.data_pagamento.strftime("%d/%m/%Y") if self.data_pagamento else "",
            "valor_pago":      self.valor_pago or 0.0,
            "obs":             self.obs or "",
            "centro_custo":    self.centro_custo    or "",
            "forma_pagamento": self.forma_pagamento or "",
            "conta_nome":      self.conta_nome      or "",
            "valor_lancado":   round(sum(l.valor for l in self.lancamentos), 2),
        }


# ══════════════════════════════════════════════════════════════════════════════
# MÓDULO: CONFIGURAÇÕES FINANCEIRAS
# ══════════════════════════════════════════════════════════════════════════════

class CentroCusto(Base):
    __tablename__ = "centros_custo"

    id:     Mapped[int]        = mapped_column(Integer, primary_key=True, autoincrement=True)
    nome:   Mapped[str]        = mapped_column(String(100), nullable=False)
    codigo: Mapped[str | None] = mapped_column(String(20))
    tipo:   Mapped[str]        = mapped_column(String(10), default="ambos")  # receita|despesa|ambos
    ativo:  Mapped[bool]       = mapped_column(Boolean, default=True)

    def to_dict(self) -> dict:
        return {"id": self.id, "nome": self.nome, "codigo": self.codigo or "",
                "tipo": self.tipo, "ativo": self.ativo}


class CategoriaFinanceira(Base):
    __tablename__ = "categorias_financeiras"

    id:    Mapped[int]  = mapped_column(Integer, primary_key=True, autoincrement=True)
    nome:  Mapped[str]  = mapped_column(String(100), nullable=False)
    tipo:  Mapped[str]  = mapped_column(String(10), default="ambos")  # pagar|receber|ambos
    ativo: Mapped[bool] = mapped_column(Boolean, default=True)

    def to_dict(self) -> dict:
        return {"id": self.id, "nome": self.nome, "tipo": self.tipo, "ativo": self.ativo}


class FormaPagamento(Base):
    __tablename__ = "formas_pagamento"

    id:    Mapped[int]  = mapped_column(Integer, primary_key=True, autoincrement=True)
    nome:  Mapped[str]  = mapped_column(String(100), nullable=False)
    ativo: Mapped[bool] = mapped_column(Boolean, default=True)

    def to_dict(self) -> dict:
        return {"id": self.id, "nome": self.nome, "ativo": self.ativo}


# ══════════════════════════════════════════════════════════════════════════════
# MÓDULO: CLIENTES / FORNECEDORES
# ══════════════════════════════════════════════════════════════════════════════

class Contato(Base):
    """Cliente, fornecedor ou contato geral."""
    __tablename__ = "contatos"

    id:            Mapped[int]        = mapped_column(Integer, primary_key=True, autoincrement=True)
    tipo:          Mapped[str]        = mapped_column(String(12), default="fornecedor")  # cliente|fornecedor|ambos
    nome:          Mapped[str]        = mapped_column(String(300), nullable=False)
    nome_fantasia: Mapped[str | None] = mapped_column(String(300))
    cnpj:          Mapped[str | None] = mapped_column(String(18))
    cpf:           Mapped[str | None] = mapped_column(String(14))
    ie:            Mapped[str | None] = mapped_column(String(30))
    email:         Mapped[str | None] = mapped_column(String(200))
    telefone:      Mapped[str | None] = mapped_column(String(20))
    celular:       Mapped[str | None] = mapped_column(String(20))
    logradouro:    Mapped[str | None] = mapped_column(String(300))
    numero:        Mapped[str | None] = mapped_column(String(10))
    complemento:   Mapped[str | None] = mapped_column(String(100))
    bairro:        Mapped[str | None] = mapped_column(String(100))
    cidade:        Mapped[str | None] = mapped_column(String(100))
    estado:        Mapped[str | None] = mapped_column(String(2))
    cep:           Mapped[str | None] = mapped_column(String(9))
    obs:           Mapped[str | None] = mapped_column(Text)
    ativo:         Mapped[bool]       = mapped_column(Boolean, default=True)
    situacao_rf:   Mapped[str | None] = mapped_column(String(80))
    criado_em:     Mapped[datetime]   = mapped_column(DateTime, default=datetime.now)
    atualizado_em: Mapped[datetime]   = mapped_column(
        DateTime, default=datetime.now, onupdate=datetime.now)

    def to_dict(self) -> dict:
        return {
            "id":            self.id,
            "tipo":          self.tipo,
            "nome":          self.nome,
            "nome_fantasia": self.nome_fantasia or "",
            "cnpj":          self.cnpj or "",
            "cpf":           self.cpf  or "",
            "ie":            self.ie   or "",
            "email":         self.email    or "",
            "telefone":      self.telefone or "",
            "celular":       self.celular  or "",
            "logradouro":    self.logradouro  or "",
            "numero":        self.numero      or "",
            "complemento":   self.complemento or "",
            "bairro":        self.bairro  or "",
            "cidade":        self.cidade  or "",
            "estado":        self.estado  or "",
            "cep":           self.cep     or "",
            "obs":           self.obs     or "",
            "ativo":         self.ativo,
            "situacao_rf":   self.situacao_rf or "",
            "criado_em":     self.criado_em.strftime("%d/%m/%Y") if self.criado_em else "",
        }


class ContaFinanceira(Base):
    __tablename__ = "contas_financeiras"

    id:            Mapped[int]        = mapped_column(Integer, primary_key=True, autoincrement=True)
    nome:          Mapped[str]        = mapped_column(String(100), nullable=False)
    tipo:          Mapped[str]        = mapped_column(String(20), default="corrente")  # caixa|corrente|poupanca|outro
    saldo_inicial: Mapped[float]      = mapped_column(Float, default=0.0)
    banco:         Mapped[str | None] = mapped_column(String(100))
    agencia:       Mapped[str | None] = mapped_column(String(20))
    conta_numero:  Mapped[str | None] = mapped_column(String(30))
    ativo:         Mapped[bool]       = mapped_column(Boolean, default=True)

    def to_dict(self) -> dict:
        return {
            "id": self.id, "nome": self.nome, "tipo": self.tipo,
            "saldo_inicial": self.saldo_inicial or 0.0,
            "banco": self.banco or "", "agencia": self.agencia or "",
            "conta_numero": self.conta_numero or "", "ativo": self.ativo,
        }


# ══════════════════════════════════════════════════════════════════════════════
# MÓDULO: FECHAMENTO DE CAIXA
# ══════════════════════════════════════════════════════════════════════════════

class MaquininhaCaixa(Base):
    """Configuração pré-definida das maquininhas (feita uma vez nas Configurações)."""
    __tablename__ = "maquininhas_caixa"

    id:            Mapped[int]        = mapped_column(Integer, primary_key=True, autoincrement=True)
    nome:          Mapped[str]        = mapped_column(String(100), nullable=False)
    conta_destino: Mapped[str | None] = mapped_column(String(100))
    ordem:         Mapped[int]        = mapped_column(Integer, default=0)
    ativo:         Mapped[bool]       = mapped_column(Boolean, default=True)

    def to_dict(self) -> dict:
        return {
            "id":            self.id,
            "nome":          self.nome,
            "conta_destino": self.conta_destino or "",
            "ordem":         self.ordem,
            "ativo":         self.ativo,
        }


class FechamentoCaixa(Base):
    """Um registro por fechamento de turno."""
    __tablename__ = "fechamentos_caixa"

    id:             Mapped[int]        = mapped_column(Integer, primary_key=True, autoincrement=True)
    data:           Mapped[date]       = mapped_column(Date, nullable=False)
    turno_id:       Mapped[int | None] = mapped_column(
        Integer, ForeignKey("turnos.id", ondelete="SET NULL"), nullable=True
    )
    operador_id:    Mapped[str | None] = mapped_column(
        String(36), ForeignKey("colaboradores.id", ondelete="SET NULL"), nullable=True
    )
    apoio_id:       Mapped[str | None] = mapped_column(
        String(36), ForeignKey("colaboradores.id", ondelete="SET NULL"), nullable=True
    )
    valor_envelope: Mapped[float]      = mapped_column(Float, default=0.0)
    # Cédulas (quantidade)
    cedulas_200:    Mapped[int]        = mapped_column(Integer, default=0)
    cedulas_100:    Mapped[int]        = mapped_column(Integer, default=0)
    cedulas_50:     Mapped[int]        = mapped_column(Integer, default=0)
    cedulas_20:     Mapped[int]        = mapped_column(Integer, default=0)
    cedulas_10:     Mapped[int]        = mapped_column(Integer, default=0)
    cedulas_5:      Mapped[int]        = mapped_column(Integer, default=0)
    cedulas_2:      Mapped[int]        = mapped_column(Integer, default=0)
    # Moedas (quantidade)
    moedas_100:     Mapped[int]        = mapped_column(Integer, default=0)  # R$1,00
    moedas_050:     Mapped[int]        = mapped_column(Integer, default=0)  # R$0,50
    moedas_025:     Mapped[int]        = mapped_column(Integer, default=0)  # R$0,25
    moedas_010:     Mapped[int]        = mapped_column(Integer, default=0)  # R$0,10
    moedas_005:     Mapped[int]        = mapped_column(Integer, default=0)  # R$0,05
    # Dados ERP
    erp_dinheiro:   Mapped[float]      = mapped_column(Float, default=0.0)
    erp_credito:    Mapped[float]      = mapped_column(Float, default=0.0)
    erp_debito:     Mapped[float]      = mapped_column(Float, default=0.0)
    erp_pix:        Mapped[float]      = mapped_column(Float, default=0.0)
    sangria:        Mapped[float]      = mapped_column(Float, default=0.0)
    conta_dinheiro: Mapped[str | None] = mapped_column(String(100))
    conta_moedas:   Mapped[str | None] = mapped_column(String(100))
    # Divergências calculadas e gravadas
    div_dinheiro:   Mapped[float]      = mapped_column(Float, default=0.0)
    div_credito:    Mapped[float]      = mapped_column(Float, default=0.0)
    div_debito:     Mapped[float]      = mapped_column(Float, default=0.0)
    div_pix:        Mapped[float]      = mapped_column(Float, default=0.0)
    obs:            Mapped[str | None] = mapped_column(Text)
    criado_em:      Mapped[datetime]   = mapped_column(DateTime, default=datetime.now)
    editado_em:     Mapped[datetime | None] = mapped_column(DateTime, nullable=True)
    log_edicao:     Mapped[str | None] = mapped_column(Text)

    turno: Mapped["Turno | None"] = relationship("Turno", foreign_keys=[turno_id])
    operador: Mapped["Colaborador | None"] = relationship(
        "Colaborador",
        primaryjoin="FechamentoCaixa.operador_id == Colaborador.id",
        foreign_keys="[FechamentoCaixa.operador_id]",
    )
    apoio: Mapped["Colaborador | None"] = relationship(
        "Colaborador",
        primaryjoin="FechamentoCaixa.apoio_id == Colaborador.id",
        foreign_keys="[FechamentoCaixa.apoio_id]",
    )
    lancamentos_maq: Mapped[list["LancamentoMaquininha"]] = relationship(
        back_populates="fechamento", cascade="all, delete-orphan"
    )

    def to_dict(self) -> dict:
        total_ced = (
            self.cedulas_200 * 200 + self.cedulas_100 * 100 +
            self.cedulas_50 * 50   + self.cedulas_20 * 20 +
            self.cedulas_10 * 10   + self.cedulas_5 * 5 + self.cedulas_2 * 2
        )
        total_moe = round(
            self.moedas_100 * 1.0  + self.moedas_050 * 0.5 +
            self.moedas_025 * 0.25 + self.moedas_010 * 0.1 + self.moedas_005 * 0.05,
            2,
        )
        total_din  = round(total_ced + total_moe, 2)
        total_cred = round(sum(l.credito for l in self.lancamentos_maq), 2)
        total_deb  = round(sum(l.debito  for l in self.lancamentos_maq), 2)
        total_pix  = round(sum(l.pix     for l in self.lancamentos_maq), 2)
        divs   = [self.div_dinheiro, self.div_credito, self.div_debito, self.div_pix]
        status = "OK" if all(abs(d) < 0.005 for d in divs) else "DIVERGÊNCIA"
        return {
            "id":             self.id,
            "data":           self.data.strftime("%d/%m/%Y") if self.data else "",
            "data_iso":       self.data.isoformat() if self.data else "",
            "turno_id":       self.turno_id,
            "turno_nome":     self.turno.nome if self.turno else "",
            "operador_id":    self.operador_id,
            "operador_nome":  (self.operador.apelido or self.operador.nome) if self.operador else "",
            "apoio_id":       self.apoio_id,
            "apoio_nome":     (self.apoio.apelido or self.apoio.nome) if self.apoio else "",
            "valor_envelope": self.valor_envelope,
            "cedulas_200": self.cedulas_200, "cedulas_100": self.cedulas_100,
            "cedulas_50":  self.cedulas_50,  "cedulas_20":  self.cedulas_20,
            "cedulas_10":  self.cedulas_10,  "cedulas_5":   self.cedulas_5,
            "cedulas_2":   self.cedulas_2,
            "moedas_100":  self.moedas_100,  "moedas_050":  self.moedas_050,
            "moedas_025":  self.moedas_025,  "moedas_010":  self.moedas_010,
            "moedas_005":  self.moedas_005,
            "total_cedulas":     total_ced,
            "total_moedas":      total_moe,
            "total_dinheiro":    total_din,
            "erp_dinheiro":      self.erp_dinheiro,
            "erp_credito":       self.erp_credito,
            "erp_debito":        self.erp_debito,
            "erp_pix":           self.erp_pix,
            "sangria":           self.sangria,
            "conta_dinheiro":    self.conta_dinheiro or "",
            "conta_moedas":      self.conta_moedas   or "",
            "div_dinheiro":      self.div_dinheiro,
            "div_credito":       self.div_credito,
            "div_debito":        self.div_debito,
            "div_pix":           self.div_pix,
            "total_maq_credito": total_cred,
            "total_maq_debito":  total_deb,
            "total_maq_pix":     total_pix,
            "obs":        self.obs or "",
            "criado_em":  self.criado_em.strftime("%d/%m/%Y %H:%M") if self.criado_em else "",
            "editado_em": self.editado_em.strftime("%d/%m/%Y %H:%M") if self.editado_em else "",
            "log_edicao": self.log_edicao or "",
            "status":     status,
            "lancamentos_maq": [
                {
                    "maquininha_id":   l.maquininha_id,
                    "maquininha_nome": l.maquininha.nome if l.maquininha else "",
                    "credito": l.credito, "debito": l.debito, "pix": l.pix,
                    "total":   round(l.credito + l.debito + l.pix, 2),
                }
                for l in self.lancamentos_maq
            ],
        }


class LancamentoMaquininha(Base):
    """N registros por fechamento, 1 por máquina."""
    __tablename__ = "lancamentos_maquininha"

    id:            Mapped[int]   = mapped_column(Integer, primary_key=True, autoincrement=True)
    fechamento_id: Mapped[int]   = mapped_column(
        Integer, ForeignKey("fechamentos_caixa.id", ondelete="CASCADE"), nullable=False
    )
    maquininha_id: Mapped[int]   = mapped_column(
        Integer, ForeignKey("maquininhas_caixa.id", ondelete="CASCADE"), nullable=False
    )
    credito:       Mapped[float] = mapped_column(Float, default=0.0)
    debito:        Mapped[float] = mapped_column(Float, default=0.0)
    pix:           Mapped[float] = mapped_column(Float, default=0.0)

    fechamento: Mapped["FechamentoCaixa"] = relationship(back_populates="lancamentos_maq")
    maquininha: Mapped["MaquininhaCaixa"] = relationship()
