# -*- coding: utf-8; -*-
################################################################################
#
#  WuttaFarm --Web app to integrate with and extend farmOS
#  Copyright © 2026 Lance Edgar
#
#  This file is part of WuttaFarm.
#
#  WuttaFarm is free software: you can redistribute it and/or modify it under
#  the terms of the GNU General Public License as published by the Free Software
#  Foundation, either version 3 of the License, or (at your option) any later
#  version.
#
#  WuttaFarm is distributed in the hope that it will be useful, but WITHOUT ANY
#  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
#  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License along with
#  WuttaFarm.  If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Model definition for Quantities
"""

import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declared_attr
from sqlalchemy.ext.associationproxy import association_proxy

from wuttjamaican.db import model


class QuantityType(model.Base):
    """
    Represents an "quantity type" from farmOS
    """

    __tablename__ = "quantity_type"
    __versioned__ = {}
    __wutta_hint__ = {
        "model_title": "Quantity Type",
        "model_title_plural": "Quantity Types",
    }

    uuid = model.uuid_column()

    name = sa.Column(
        sa.String(length=100),
        nullable=False,
        unique=True,
        doc="""
        Name of the quantity type.
        """,
    )

    description = sa.Column(
        sa.String(length=255),
        nullable=True,
        doc="""
        Description for the quantity type.
        """,
    )

    farmos_uuid = sa.Column(
        model.UUID(),
        nullable=True,
        unique=True,
        doc="""
        UUID for the quantity type within farmOS.
        """,
    )

    drupal_id = sa.Column(
        sa.String(length=50),
        nullable=True,
        unique=True,
        doc="""
        Drupal internal ID for the quantity type.
        """,
    )

    def __str__(self):
        return self.name or ""


class Quantity(model.Base):
    """
    Represents a base quantity record from farmOS
    """

    __tablename__ = "quantity"
    __versioned__ = {}
    __wutta_hint__ = {
        "model_title": "Quantity",
        "model_title_plural": "All Quantities",
    }

    uuid = model.uuid_column()

    quantity_type_id = sa.Column(
        sa.String(length=50),
        sa.ForeignKey("quantity_type.drupal_id"),
        nullable=False,
    )

    quantity_type = orm.relationship(QuantityType)

    measure_id = sa.Column(
        sa.String(length=20),
        sa.ForeignKey("measure.drupal_id"),
        nullable=False,
        doc="""
        Measure for the quantity.
        """,
    )

    measure = orm.relationship("Measure")

    value_numerator = sa.Column(
        sa.Integer(),
        nullable=False,
        doc="""
        Numerator for the quantity value.
        """,
    )

    value_denominator = sa.Column(
        sa.Integer(),
        nullable=False,
        doc="""
        Denominator for the quantity value.
        """,
    )

    units_uuid = model.uuid_fk_column("unit.uuid", nullable=False)
    units = orm.relationship("Unit")

    label = sa.Column(
        sa.String(length=255),
        nullable=True,
        doc="""
        Optional label for the quantity.
        """,
    )

    farmos_uuid = sa.Column(
        model.UUID(),
        nullable=True,
        unique=True,
        doc="""
        UUID for the quantity within farmOS.
        """,
    )

    drupal_id = sa.Column(
        sa.Integer(),
        nullable=True,
        unique=True,
        doc="""
        Drupal internal ID for the quantity.
        """,
    )

    _log = orm.relationship(
        "LogQuantity",
        uselist=False,
        cascade="all, delete-orphan",
        cascade_backrefs=False,
        back_populates="quantity",
    )

    def make_log_quantity(log):
        from wuttafarm.db.model import LogQuantity

        return LogQuantity(log=log)

    log = association_proxy(
        "_log",
        "log",
        creator=make_log_quantity,
    )

    def get_value_decimal(self):
        # TODO: should actually return a decimal here?
        return self.value_numerator / self.value_denominator

    def render_as_text(self, config=None):
        measure = str(self.measure or self.measure_id or "")
        value = self.get_value_decimal()
        if config:
            app = config.get_app()
            value = app.render_quantity(value)
        units = str(self.units or "")
        return f"( {measure} ) {value} {units}"

    def __str__(self):
        return self.render_as_text()


class QuantityMixin:

    uuid = model.uuid_fk_column("quantity.uuid", nullable=False, primary_key=True)

    @declared_attr
    def quantity(cls):
        return orm.relationship(
            Quantity,
            single_parent=True,
            cascade="all, delete-orphan",
            cascade_backrefs=False,
        )

    def get_value_decimal(self):
        return self.quantity.get_value_decimal()

    def render_as_text(self, config=None):
        return self.quantity.render_as_text(config)

    def __str__(self):
        return self.render_as_text()


def add_quantity_proxies(subclass):
    Quantity.make_proxy(subclass, "quantity", "farmos_uuid")
    Quantity.make_proxy(subclass, "quantity", "drupal_id")
    Quantity.make_proxy(subclass, "quantity", "quantity_type")
    Quantity.make_proxy(subclass, "quantity", "quantity_type_id")
    Quantity.make_proxy(subclass, "quantity", "measure")
    Quantity.make_proxy(subclass, "quantity", "measure_id")
    Quantity.make_proxy(subclass, "quantity", "value_numerator")
    Quantity.make_proxy(subclass, "quantity", "value_denominator")
    Quantity.make_proxy(subclass, "quantity", "value_decimal")
    Quantity.make_proxy(subclass, "quantity", "units_uuid")
    Quantity.make_proxy(subclass, "quantity", "units")
    Quantity.make_proxy(subclass, "quantity", "label")
    Quantity.make_proxy(subclass, "quantity", "log")


class StandardQuantity(QuantityMixin, model.Base):
    """
    Represents a Standard Quantity from farmOS
    """

    __tablename__ = "quantity_standard"
    __versioned__ = {}
    __wutta_hint__ = {
        "model_title": "Standard Quantity",
        "model_title_plural": "Standard Quantities",
        "farmos_quantity_type": "standard",
    }


add_quantity_proxies(StandardQuantity)


class MaterialQuantity(QuantityMixin, model.Base):
    """
    Represents a Material Quantity from farmOS
    """

    __tablename__ = "quantity_material"
    __versioned__ = {}
    __wutta_hint__ = {
        "model_title": "Material Quantity",
        "model_title_plural": "Material Quantities",
        "farmos_quantity_type": "material",
    }

    _material_types = orm.relationship(
        "MaterialQuantityMaterialType",
        cascade="all, delete-orphan",
        cascade_backrefs=False,
        back_populates="quantity",
    )

    material_types = association_proxy(
        "_material_types",
        "material_type",
        creator=lambda mtype: MaterialQuantityMaterialType(material_type=mtype),
    )

    def render_as_text(self, config=None):
        text = super().render_as_text(config)
        mtypes = ", ".join([str(mt) for mt in self.material_types])
        return f"{mtypes} {text}"


add_quantity_proxies(MaterialQuantity)


class MaterialQuantityMaterialType(model.Base):
    """
    Represents a "material quantity's material type relationship" from
    farmOS.
    """

    __tablename__ = "quantity_material_material_type"
    __versioned__ = {}

    uuid = model.uuid_column()

    quantity_uuid = model.uuid_fk_column("quantity_material.uuid", nullable=False)
    quantity = orm.relationship(
        MaterialQuantity,
        foreign_keys=quantity_uuid,
        back_populates="_material_types",
    )

    material_type_uuid = model.uuid_fk_column("material_type.uuid", nullable=False)
    material_type = orm.relationship(
        "MaterialType",
        foreign_keys=material_type_uuid,
        back_populates="_quantities",
    )
