# -*- 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 Assets
"""

from collections import OrderedDict

from webhelpers2.html import tags

from wuttaweb.forms.schema import WuttaDictEnum
from wuttaweb.db import Session

from wuttafarm.web.views import WuttaFarmMasterView
from wuttafarm.db.model import Asset, Log
from wuttafarm.web.forms.schema import OwnerRefs, AssetRefs
from wuttafarm.web.forms.widgets import ImageWidget
from wuttafarm.util import get_log_type_enum
from wuttafarm.web.util import get_farmos_client_for_user


def get_asset_type_enum(config):
    app = config.get_app()
    model = app.model
    session = Session()
    asset_types = OrderedDict()
    query = session.query(model.AssetType).order_by(model.AssetType.name)
    for asset_type in query:
        asset_types[asset_type.drupal_id] = asset_type.name
    return asset_types


class AssetTypeMasterView(WuttaFarmMasterView):
    """
    Base class for "Asset Type" master views.

    A bit of a misnmer perhaps, this is *not* for the actual AssetType
    model, but rather the "secondary" types, e.g.  AnimalType,
    LandType etc.
    """


class AssetMasterView(WuttaFarmMasterView):
    """
    Base class for Asset master views
    """

    farmos_entity_type = "asset"

    labels = {
        "groups": "Group Membership",
    }

    sort_defaults = "asset_name"

    filter_defaults = {
        "asset_name": {"active": True, "verb": "contains"},
        "archived": {"active": True, "verb": "is_false"},
    }

    form_fields = [
        "asset_name",
        "parents",
        "notes",
        "asset_type",
        "owners",
        "is_location",
        "is_fixed",
        "locations",
        "groups",
        "archived",
        "drupal_id",
        "farmos_uuid",
        "thumbnail_url",
        "image_url",
        "thumbnail",
        "image",
    ]

    has_rows = True
    row_model_class = Log
    rows_viewable = True

    row_labels = {
        "message": "Log Name",
    }

    row_grid_columns = [
        "status",
        "drupal_id",
        "timestamp",
        "message",
        "log_type",
        "assets",
        "location",
        "quantity",
        "is_group_assignment",
    ]

    rows_sort_defaults = ("timestamp", "desc")

    def get_fallback_templates(self, template):
        templates = super().get_fallback_templates(template)

        if self.viewing:
            templates.insert(0, "/assets/master/view.mako")

        return templates

    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.Asset:
            query = query.join(model.Asset)
        return query

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

        # thumbnail
        g.set_renderer("thumbnail", self.render_grid_thumbnail)
        g.set_label("thumbnail", "", column_only=True)
        g.set_centered("thumbnail")

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

        # asset_name
        g.set_link("asset_name")
        g.set_sorter("asset_name", model.Asset.asset_name)
        g.set_filter("asset_name", model.Asset.asset_name)

        # parents
        g.set_renderer("parents", self.render_parents_for_grid)

        # groups
        g.set_renderer("groups", self.render_groups_for_grid)

        # owners
        g.set_label("owners", "Owner")
        g.set_renderer("owners", self.render_owners_for_grid)

        # locations
        g.set_label("locations", "Location")
        g.set_renderer("locations", self.render_locations_for_grid)

        # archived
        g.set_renderer("archived", "boolean")
        g.set_sorter("archived", model.Asset.archived)
        g.set_filter("archived", model.Asset.archived)

    def render_parents_for_grid(self, asset, field, value):

        if self.farmos_style_grid_links:
            links = []
            for parent in asset.parents:
                url = self.request.route_url(
                    f"{parent.asset_type}_assets.view", uuid=parent.uuid
                )
                links.append(tags.link_to(str(parent), url))
            return ", ".join(links)

        parents = [str(p.parent) for p in asset.parents]
        return ", ".join(parents)

    def render_owners_for_grid(self, asset, field, value):

        if self.farmos_style_grid_links:
            links = []
            for user in asset.owners:
                url = self.request.route_url("users.view", uuid=user.uuid)
                links.append(tags.link_to(user.username, url))
            return ", ".join(links)

        return ", ".join([user.username for user in asset.owners])

    def render_groups_for_grid(self, asset, field, value):
        asset_handler = self.app.get_asset_handler()
        groups = asset_handler.get_groups(asset)

        if self.farmos_style_grid_links:
            links = []
            for group in groups:
                url = self.request.route_url(
                    f"{group.asset_type}_assets.view", uuid=group.uuid
                )
                links.append(tags.link_to(str(group), url))
            return ", ".join(links)

        return ", ".join([str(group) for group in groups])

    def render_locations_for_grid(self, asset, field, value):
        asset_handler = self.app.get_asset_handler()
        locations = asset_handler.get_locations(asset)

        if self.farmos_style_grid_links:
            links = []
            for loc in locations:
                url = self.request.route_url(
                    f"{loc.asset_type}_assets.view", uuid=loc.uuid
                )
                links.append(tags.link_to(str(loc), url))
            return ", ".join(links)

        return ", ".join([str(loc) for loc in locations])

    def grid_row_class(self, asset, data, i):
        """ """
        if asset.archived:
            return "has-background-warning"
        return None

    def configure_form(self, form):
        f = form
        super().configure_form(f)
        asset_handler = self.app.get_asset_handler()
        asset = form.model_instance

        # asset_type
        if self.creating:
            f.remove("asset_type")
        else:
            f.set_node(
                "asset_type",
                WuttaDictEnum(self.request, get_asset_type_enum(self.config)),
            )
            f.set_readonly("asset_type")

        # owners
        if self.creating or self.editing:
            f.remove("owners")  # TODO: need to support this
        else:
            f.set_node("owners", OwnerRefs(self.request))
            # nb. must explicity declare value for non-standard field
            f.set_default("owners", asset.owners)

        # locations
        if self.creating or self.editing:
            # nb. this is a calculated field
            f.remove("locations")
        else:
            f.set_label("locations", "Current Location")
            f.set_node("locations", AssetRefs(self.request))
            # nb. must explicity declare value for non-standard field
            f.set_default("locations", asset_handler.get_locations(asset))

        # groups
        if self.creating or self.editing:
            # nb. this is a calculated field
            f.remove("groups")
        else:
            f.set_node("groups", AssetRefs(self.request))
            # nb. must explicity declare value for non-standard field
            f.set_default("groups", asset_handler.get_groups(asset))

        # parents
        f.set_node("parents", AssetRefs(self.request, for_asset=asset))
        f.set_required("parents", False)
        if not self.creating:
            # nb. must explicity declare value for non-standard field
            f.set_default("parents", asset.parents)

        # notes
        f.set_widget("notes", "notes")

        # thumbnail_url
        if self.creating or self.editing:
            f.remove("thumbnail_url")

        # image_url
        if self.creating or self.editing:
            f.remove("image_url")

        # thumbnail
        if self.creating or self.editing:
            f.remove("thumbnail")
        elif asset.thumbnail_url:
            f.set_widget("thumbnail", ImageWidget("animal thumbnail"))
            f.set_default("thumbnail", asset.thumbnail_url)

        # image
        if self.creating or self.editing:
            f.remove("image")
        elif asset.image_url:
            f.set_widget("image", ImageWidget("animal image"))
            f.set_default("image", asset.image_url)

    def objectify(self, form):
        model = self.app.model
        session = self.Session()
        asset = super().objectify(form)
        data = form.validated

        if self.creating:
            asset.asset_type = self.get_asset_type()

        current = [p.uuid for p in asset.parents]
        desired = data["parents"] or []

        for uuid in desired:
            if uuid not in current:
                parent = session.get(model.Asset, uuid)
                assert parent
                asset.parents.append(parent)

        for uuid in current:
            if uuid not in desired:
                parent = session.get(model.Asset, uuid)
                assert parent
                asset.parents.remove(parent)

        return asset

    def get_asset_type(self):
        model_class = self.get_model_class()
        return model_class.__wutta_hint__["farmos_asset_type"]

    def get_farmos_url(self, asset):
        return self.app.get_farmos_url(f"/asset/{asset.drupal_id}")

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

        if asset.farmos_uuid:
            asset_type = self.get_asset_type()
            route = f"farmos_{asset_type}_assets.view"
            url = self.request.route_url(route, uuid=asset.farmos_uuid)
            buttons.append(
                self.make_button(
                    "View farmOS record", primary=True, url=url, icon_left="eye"
                )
            )

        return buttons

    def get_version_joins(self):
        """
        We override this to declare the relationship between the
        view's data model (which is some type of asset table) and the
        canonical ``Asset`` model, so the revision history views
        include transactions which reference either version table.

        See also parent method,
        :meth:`~wuttaweb:wuttaweb.views.master.MasterView.get_version_joins()`
        """
        model = self.app.model
        return super().get_version_joins() + [
            model.Asset,
        ]

    def get_row_grid_data(self, asset):
        model = self.app.model
        session = self.Session()
        return (
            session.query(model.Log)
            .outerjoin(model.LogAsset)
            .filter(model.LogAsset.asset_uuid == asset.uuid)
        )

    def configure_row_grid(self, grid):
        g = grid
        super().configure_row_grid(g)
        enum = self.app.enum
        model = self.app.model
        session = self.Session()

        # status
        g.set_enum("status", enum.LOG_STATUS)

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

        # message
        g.set_link("message")
        g.set_sorter("message", model.Log.message)
        g.set_filter("message", model.Log.message)

        # timestamp
        g.set_sorter("timestamp", model.Log.timestamp)
        g.set_filter("timestamp", model.Log.timestamp)

        # log_type
        g.set_sorter("log_type", model.Log.log_type)
        g.set_filter("log_type", model.Log.log_type)
        g.set_enum("log_type", get_log_type_enum(self.config, session=session))

        # assets
        g.set_renderer("assets", self.render_assets_for_grid)

    def render_assets_for_grid(self, log, field, value):
        assets = getattr(log, field)

        if self.farmos_style_grid_links:
            links = []
            for asset in 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 ", ".join([str(a) for a in assets])

    def get_row_action_url_view(self, log, i):
        return self.request.route_url(f"logs_{log.log_type}.view", uuid=log.uuid)


class AllAssetView(AssetMasterView):
    """
    Master view for Assets
    """

    model_class = Asset
    route_prefix = "assets"
    url_prefix = "/assets"

    farmos_refurl_path = "/assets"

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

    grid_columns = [
        "thumbnail",
        "drupal_id",
        "asset_name",
        "groups",
        "asset_type",
        "parents",
        "owners",
        "locations",
        "archived",
    ]

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

        # asset_type
        g.set_enum("asset_type", get_asset_type_enum(self.config))

        # view action links to final asset record
        def asset_url(asset, i):
            return self.request.route_url(
                f"{asset.asset_type}_assets.view", uuid=asset.uuid
            )

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


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

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


def includeme(config):
    defaults(config)
