# -*- 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/>.
#
################################################################################
"""
Master view for Quantities
"""

from collections import OrderedDict

from webhelpers2.html import tags

from wuttaweb.db import Session

from wuttafarm.web.views import WuttaFarmMasterView
from wuttafarm.db.model import (
    QuantityType,
    Quantity,
    StandardQuantity,
    MaterialQuantity,
)
from wuttafarm.web.forms.schema import UnitRef, LogRef, MaterialTypeRefs
from wuttafarm.util import get_log_type_enum


def get_quantity_type_enum(config):
    app = config.get_app()
    model = app.model
    session = Session()
    quantity_types = OrderedDict()
    query = session.query(model.QuantityType).order_by(model.QuantityType.name)
    for quantity_type in query:
        quantity_types[quantity_type.drupal_id] = quantity_type.name
    return quantity_types


class QuantityTypeView(WuttaFarmMasterView):
    """
    Master view for Quantity Types
    """

    model_class = QuantityType
    route_prefix = "quantity_types"
    url_prefix = "/quantity-types"

    grid_columns = [
        "name",
        "description",
    ]

    sort_defaults = "name"

    filter_defaults = {
        "name": {"active": True, "verb": "contains"},
    }

    form_fields = [
        "name",
        "description",
        "drupal_id",
        "farmos_uuid",
    ]

    def configure_grid(self, grid):
        g = grid
        super().configure_grid(g)

        # name
        g.set_link("name")

    def get_xref_buttons(self, quantity_type):
        buttons = super().get_xref_buttons(quantity_type)

        if quantity_type.farmos_uuid:
            buttons.append(
                self.make_button(
                    "View farmOS record",
                    primary=True,
                    url=self.request.route_url(
                        "farmos_quantity_types.view", uuid=quantity_type.farmos_uuid
                    ),
                    icon_left="eye",
                )
            )

        return buttons


class QuantityMasterView(WuttaFarmMasterView):
    """
    Base class for Quantity master views
    """

    farmos_entity_type = "quantity"

    labels = {
        "log_id": "Log ID",
    }

    grid_columns = [
        "drupal_id",
        "log_id",
        "log_status",
        "log_timestamp",
        "log_type",
        "log_name",
        "log_assets",
        "measure",
        "value",
        "units",
        "label",
        "quantity_type",
    ]

    sort_defaults = ("log_timestamp", "desc")

    form_fields = [
        "quantity_type",
        "as_text",
        "measure",
        "value",
        "units",
        "label",
        "log",
        "drupal_id",
        "farmos_uuid",
    ]

    def get_query(self, session=None):
        """ """
        model = self.app.model
        model_class = self.get_model_class()
        session = session or self.Session()

        query = session.query(model_class)
        if model_class is not model.Quantity:
            query = query.join(model.Quantity)

        query = query.join(model.Measure).join(model.Unit)

        query = query.outerjoin(model.LogQuantity).outerjoin(model.Log)

        return query

    def configure_grid(self, grid):
        g = grid
        super().configure_grid(g)
        model = self.app.model
        model_class = self.get_model_class()
        session = self.Session()

        # drupal_id
        g.set_label("drupal_id", "ID", column_only=True)
        g.set_sorter("drupal_id", model.Quantity.drupal_id)

        # log_id
        g.set_renderer("log_id", self.render_log_id_for_grid)
        g.set_sorter("log_id", model.Log.drupal_id)

        # log_status
        g.set_renderer("log_status", self.render_log_status_for_grid)
        g.set_sorter("log_status", model.Log.status)

        # log_timestamp
        g.set_renderer("log_timestamp", self.render_log_timestamp_for_grid)
        g.set_sorter("log_timestamp", model.Log.timestamp)

        # log_type
        self.log_type_enum = get_log_type_enum(self.config, session)
        g.set_renderer("log_type", self.render_log_type_for_grid)
        g.set_sorter("log_type", model.Log.log_type)

        # log_name
        g.set_renderer("log_name", self.render_log_name_for_grid)
        g.set_sorter("log_name", model.Log.message)
        if not self.farmos_style_grid_links:
            g.set_link("log_name")

        # log_assets
        g.set_renderer("log_assets", self.render_log_assets_for_grid)
        if not self.farmos_style_grid_links:
            g.set_link("log_assets")

        # quantity_type
        if model_class is not model.Quantity:
            g.remove("quantity_type")
        else:
            g.set_enum("quantity_type", get_quantity_type_enum(self.config))

        # measure
        g.set_sorter("measure", model.Measure.name)

        # value
        g.set_renderer("value", self.render_value_for_grid)

        # units
        g.set_sorter("units", model.Unit.name)

        # label
        g.set_sorter("label", model.Quantity.label)

        # view action links to final quantity record
        if model_class is model.Quantity:

            def quantity_url(quantity, i):
                return self.request.route_url(
                    f"quantities_{quantity.quantity_type_id}.view", uuid=quantity.uuid
                )

            g.add_action("view", icon="eye", url=quantity_url)

    def render_log_id_for_grid(self, quantity, field, value):
        if log := quantity.log:
            return log.drupal_id
        return None

    def render_log_status_for_grid(self, quantity, field, value):
        enum = self.app.enum
        if log := quantity.log:
            return enum.LOG_STATUS.get(log.status, log.status)
        return None

    def render_log_timestamp_for_grid(self, quantity, field, value):
        if log := quantity.log:
            return self.app.render_date(log.timestamp)
        return None

    def render_log_type_for_grid(self, quantity, field, value):
        if log := quantity.log:
            return self.log_type_enum.get(log.log_type, log.log_type)
        return None

    def render_log_name_for_grid(self, quantity, field, value):
        if log := quantity.log:
            if self.farmos_style_grid_links:
                url = self.request.route_url(f"logs_{log.log_type}.view", uuid=log.uuid)
                return tags.link_to(log.message, url)
            return log.message
        return None

    def render_log_assets_for_grid(self, quantity, field, value):
        if log := quantity.log:
            if self.farmos_style_grid_links:
                links = []
                for asset in log.assets:
                    url = self.request.route_url(
                        f"{asset.asset_type}_assets.view", uuid=asset.uuid
                    )
                    links.append(tags.link_to(str(asset), url))
                return ", ".join(links)
            return [str(a) for a in log.assets]
        return None

    def render_value_for_grid(self, quantity, field, value):
        value = quantity.value_numerator / quantity.value_denominator
        return self.app.render_quantity(value)

    def get_instance_title(self, quantity):
        return quantity.render_as_text(self.config)

    def configure_form(self, form):
        f = form
        super().configure_form(f)
        quantity = form.model_instance

        # as_text
        if self.creating or self.editing:
            f.remove("as_text")
        else:
            f.set_default("as_text", quantity.render_as_text(self.config))

        # quantity_type
        if self.creating:
            f.remove("quantity_type")
        else:
            f.set_readonly("quantity_type")
            f.set_default("quantity_type", quantity.quantity_type.name)

        # measure
        if self.creating:
            f.remove("measure")
        else:
            f.set_readonly("measure")
            f.set_default("measure", quantity.measure.name)

        # value
        if self.creating:
            f.remove("value")
        else:
            value = quantity.value_numerator / quantity.value_denominator
            value = self.app.render_quantity(value)
            f.set_default(
                "value",
                f"{value} ({quantity.value_numerator} / {quantity.value_denominator})",
            )

        # units
        if self.creating:
            f.remove("units")
        else:
            f.set_readonly("units")
            f.set_node("units", UnitRef(self.request))
            # TODO: ugh
            f.set_default("units", quantity.quantity.units)

        # log
        if self.creating or self.editing:
            f.remove("log")
        else:
            f.set_node("log", LogRef(self.request))
            f.set_default("log", quantity.log)

    def get_xref_buttons(self, quantity):
        buttons = super().get_xref_buttons(quantity)

        if quantity.farmos_uuid:
            url = self.request.route_url(
                f"farmos_quantities_{quantity.quantity_type_id}.view",
                uuid=quantity.farmos_uuid,
            )
            buttons.append(
                self.make_button(
                    "View farmOS record", primary=True, url=url, icon_left="eye"
                )
            )

        return buttons


class AllQuantityView(QuantityMasterView):
    """
    Master view for All Quantities
    """

    model_class = Quantity
    route_prefix = "quantities"
    url_prefix = "/quantities"

    viewable = False
    creatable = False
    editable = False
    deletable = False
    model_is_versioned = False

    farmos_refurl_path = "/log-quantities"


class StandardQuantityView(QuantityMasterView):
    """
    Master view for Standard Quantities
    """

    model_class = StandardQuantity
    route_prefix = "quantities_standard"
    url_prefix = "/quantities/standard"

    farmos_bundle = "standard"
    farmos_refurl_path = "/log-quantities/standard"


class MaterialQuantityView(QuantityMasterView):
    """
    Master view for Material Quantities
    """

    model_class = MaterialQuantity
    route_prefix = "quantities_material"
    url_prefix = "/quantities/material"

    farmos_bundle = "material"
    farmos_refurl_path = "/log-quantities/material"

    def configure_grid(self, grid):
        g = grid
        super().configure_grid(g)

        # material_types
        g.columns.append("material_types")
        g.set_label("material_types", "Material Type", column_only=True)
        g.set_renderer("material_types", self.render_material_types_for_grid)

    def render_material_types_for_grid(self, quantity, field, value):
        if self.farmos_style_grid_links:
            links = []
            for mtype in quantity.material_types:
                url = self.request.route_url("material_types.view", uuid=mtype.uuid)
                links.append(tags.link_to(str(mtype), url))
            return ", ".join(links)

        return ", ".join([str(mtype) for mtype in quantity.material_types])

    def configure_form(self, form):
        f = form
        super().configure_form(f)
        quantity = form.model_instance

        # material_types
        f.fields.insert_after("quantity_type", "material_types")
        f.set_node("material_types", MaterialTypeRefs(self.request))
        if not self.creating:
            f.set_default("material_types", quantity.material_types)

    def objectify(self, form):
        quantity = super().objectify(form)
        data = form.validated

        self.set_material_types(quantity, data["material_types"])

        return quantity

    def set_material_types(self, quantity, desired):
        model = self.app.model
        session = self.Session()

        current = {mt.uuid.hex: mt for mt in quantity.material_types}

        for mtype in desired:
            if mtype["uuid"] not in current:
                mtype = session.get(model.MaterialType, mtype["uuid"])
                assert mtype
                quantity.material_types.append(mtype)

        desired = [mtype["uuid"] for mtype in desired]
        for uuid, mtype in current.items():
            if uuid not in desired:
                quantity.material_types.remove(mtype)


def defaults(config, **kwargs):
    base = globals()

    QuantityTypeView = kwargs.get("QuantityTypeView", base["QuantityTypeView"])
    QuantityTypeView.defaults(config)

    AllQuantityView = kwargs.get("AllQuantityView", base["AllQuantityView"])
    AllQuantityView.defaults(config)

    StandardQuantityView = kwargs.get(
        "StandardQuantityView", base["StandardQuantityView"]
    )
    StandardQuantityView.defaults(config)

    MaterialQuantityView = kwargs.get(
        "MaterialQuantityView", base["MaterialQuantityView"]
    )
    MaterialQuantityView.defaults(config)


def includeme(config):
    defaults(config)
