"""
Fusion Accounting - Fiscal Categories

Provides a classification system for grouping general ledger accounts
into fiscal reporting categories (income, expense, asset, liability).
These categories drive structured fiscal reports and SAF-T exports,
allowing companies to map their chart of accounts onto standardised
government reporting taxonomies.

Original implementation by Nexa Systems Inc.
"""

from odoo import api, fields, models, _
from odoo.exceptions import ValidationError


class FusionFiscalCategory(models.Model):
    """
    A fiscal reporting category that groups one or more GL accounts.

    Each category carries a ``category_type`` that mirrors the four
    fundamental pillars of double-entry bookkeeping. When a SAF-T or
    Intrastat export is generated the accounts linked here determine
    which transactions are included.

    Uniqueness of ``code`` is enforced per company so that external
    auditors can refer to categories unambiguously.
    """

    _name = "fusion.fiscal.category"
    _description = "Fiscal Category"
    _order = "code, name"
    _rec_name = "display_name"

    # ------------------------------------------------------------------
    # Fields
    # ------------------------------------------------------------------
    name = fields.Char(
        string="Category Name",
        required=True,
        translate=True,
        help="Human-readable label shown in reports and menus.",
    )
    code = fields.Char(
        string="Code",
        required=True,
        help=(
            "Short alphanumeric identifier used in fiscal exports "
            "(e.g. SAF-T GroupingCode). Must be unique per company."
        ),
    )
    category_type = fields.Selection(
        selection=[
            ("income", "Income"),
            ("expense", "Expense"),
            ("asset", "Asset"),
            ("liability", "Liability"),
        ],
        string="Type",
        required=True,
        default="expense",
        help="Determines the section of the fiscal report this category appears in.",
    )
    description = fields.Text(
        string="Description",
        translate=True,
        help="Optional long description for internal documentation purposes.",
    )
    active = fields.Boolean(
        string="Active",
        default=True,
        help="Archived categories are excluded from new exports but remain on historical records.",
    )
    company_id = fields.Many2one(
        comodel_name="res.company",
        string="Company",
        required=True,
        default=lambda self: self.env.company,
        help="Company to which this fiscal category belongs.",
    )
    account_ids = fields.Many2many(
        comodel_name="account.account",
        relation="fusion_fiscal_category_account_rel",
        column1="category_id",
        column2="account_id",
        string="Accounts",
        help="General-ledger accounts assigned to this fiscal category.",
    )
    account_count = fields.Integer(
        string="# Accounts",
        compute="_compute_account_count",
        store=False,
        help="Number of accounts linked to this category.",
    )
    parent_id = fields.Many2one(
        comodel_name="fusion.fiscal.category",
        string="Parent Category",
        index=True,
        ondelete="restrict",
        help="Optional parent for hierarchical fiscal taxonomies.",
    )
    child_ids = fields.One2many(
        comodel_name="fusion.fiscal.category",
        inverse_name="parent_id",
        string="Sub-categories",
    )

    # ------------------------------------------------------------------
    # SQL constraints
    # ------------------------------------------------------------------
    _sql_constraints = [
        (
            "unique_code_per_company",
            "UNIQUE(code, company_id)",
            "The fiscal category code must be unique within each company.",
        ),
    ]

    # ------------------------------------------------------------------
    # Computed fields
    # ------------------------------------------------------------------
    @api.depends("account_ids")
    def _compute_account_count(self):
        """Count the number of accounts linked to each category."""
        for record in self:
            record.account_count = len(record.account_ids)

    @api.depends("name", "code")
    def _compute_display_name(self):
        """Build a display name combining code and name for clarity."""
        for record in self:
            if record.code:
                record.display_name = f"[{record.code}] {record.name}"
            else:
                record.display_name = record.name or ""

    # ------------------------------------------------------------------
    # Constraints
    # ------------------------------------------------------------------
    @api.constrains("parent_id")
    def _check_parent_recursion(self):
        """Prevent circular parent-child references."""
        if not self._check_recursion():
            raise ValidationError(
                _("A fiscal category cannot be its own ancestor. "
                  "Please choose a different parent.")
            )

    @api.constrains("account_ids", "company_id")
    def _check_account_company(self):
        """Ensure all linked accounts belong to the same company."""
        for record in self:
            foreign = record.account_ids.filtered(
                lambda a: a.company_id != record.company_id
            )
            if foreign:
                raise ValidationError(
                    _("All linked accounts must belong to company '%(company)s'. "
                      "The following accounts belong to a different company: %(accounts)s",
                      company=record.company_id.name,
                      accounts=", ".join(foreign.mapped("code")))
                )
