# -*- 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/>.
#
################################################################################
"""
Data normalizer for WuttaFarm / farmOS
"""

import datetime

from wuttjamaican.app import GenericHandler


class Normalizer(GenericHandler):
    """
    Base class and default implementation for the global data
    normalizer.  This should be used for normalizing records from
    WuttaFarm and/or farmOS.

    The point here is to have a single place to put the normalization
    logic, and let it be another thing which can be customized via
    subclass.
    """

    _farmos_units = None
    _farmos_measures = None

    def __init__(self, config, farmos_client=None):
        super().__init__(config)
        self.farmos_client = farmos_client

    def get_farmos_measures(self):
        if self._farmos_measures:
            return self._farmos_measures

        measures = {}
        response = self.farmos_client.session.get(
            self.app.get_farmos_url("/api/quantity/standard/resource/schema")
        )
        response.raise_for_status()
        data = response.json()
        for measure in data["definitions"]["attributes"]["properties"]["measure"][
            "oneOf"
        ]:
            measures[measure["const"]] = measure["title"]

        self._farmos_measures = measures
        return self._farmos_measures

    def get_farmos_measure_name(self, measure_id):
        measures = self.get_farmos_measures()
        return measures[measure_id]

    def get_farmos_unit(self, uuid):
        units = self.get_farmos_units()
        return units[uuid]

    def get_farmos_units(self):
        if self._farmos_units:
            return self._farmos_units

        units = {}
        result = self.farmos_client.resource.get("taxonomy_term", "unit")
        for unit in result["data"]:
            units[unit["id"]] = unit

        self._farmos_units = units
        return self._farmos_units

    def normalize_datetime(self, value):
        if not value:
            return None
        value = datetime.datetime.fromisoformat(value)
        return self.app.localtime(value)

    def normalize_farmos_asset(self, asset, included={}):
        """ """

        if notes := asset["attributes"]["notes"]:
            notes = notes["value"]

        parent_objects = []
        parent_uuids = []
        owner_objects = []
        owner_uuids = []
        if relationships := asset.get("relationships"):

            if parents := relationships.get("parent"):
                for parent in parents["data"]:
                    parent_uuid = parent["id"]
                    parent_uuids.append(parent_uuid)
                    parent_object = {
                        "uuid": parent_uuid,
                        "type": parent["type"],
                        "asset_type": parent["type"].split("--")[1],
                    }
                    if parent := included.get(parent_uuid):
                        parent_object.update(
                            {
                                "name": parent["attributes"]["name"],
                            }
                        )
                    parent_objects.append(parent_object)

            if owners := relationships.get("owner"):
                for user in owners["data"]:
                    user_uuid = user["id"]
                    owner_uuids.append(user_uuid)
                    if user := included.get(user_uuid):
                        owner_objects.append(
                            {
                                "uuid": user["id"],
                                "name": user["attributes"]["name"],
                            }
                        )

        # if self.farmos_4x:
        #     archived = asset["attributes"]["archived"]
        # else:
        #     archived = asset["attributes"]["status"] == "archived"

        return {
            "uuid": asset["id"],
            "drupal_id": asset["attributes"]["drupal_internal__id"],
            "asset_name": asset["attributes"]["name"],
            "is_location": asset["attributes"]["is_location"],
            "is_fixed": asset["attributes"]["is_fixed"],
            "archived": asset["attributes"]["archived"],
            "notes": notes,
            # nb. this is only used for certain asset types
            "produces_eggs": asset["attributes"].get("produces_eggs"),
            "parents": parent_objects,
            "parent_uuids": parent_uuids,
            "owners": owner_objects,
            "owner_uuids": owner_uuids,
        }

    def normalize_farmos_log(self, log, included={}):

        if timestamp := log["attributes"]["timestamp"]:
            timestamp = self.normalize_datetime(timestamp)

        if notes := log["attributes"]["notes"]:
            notes = notes["value"]

        log_type_object = {}
        log_type_uuid = None
        asset_objects = []
        group_objects = []
        group_uuids = []
        quantity_objects = []
        quantity_uuids = []
        location_objects = []
        location_uuids = []
        owner_objects = []
        owner_uuids = []
        if relationships := log.get("relationships"):

            if log_type := relationships.get("log_type"):
                log_type_uuid = log_type["data"]["id"]
                if log_type := included.get(log_type_uuid):
                    log_type_object = {
                        "uuid": log_type["id"],
                        "name": log_type["attributes"]["label"],
                    }

            if assets := relationships.get("asset"):
                for asset in assets["data"]:
                    asset_object = {
                        "uuid": asset["id"],
                        "type": asset["type"],
                        "asset_type": asset["type"].split("--")[1],
                    }
                    if asset := included.get(asset["id"]):
                        attrs = asset["attributes"]
                        rels = asset["relationships"]
                        asset_object.update(
                            {
                                "drupal_id": attrs["drupal_internal__id"],
                                "name": attrs["name"],
                                "is_location": attrs["is_location"],
                                "is_fixed": attrs["is_fixed"],
                                "archived": attrs["archived"],
                                "notes": attrs["notes"],
                            }
                        )
                    asset_objects.append(asset_object)

            if groups := relationships.get("group"):
                for group in groups["data"]:
                    group_uuid = group["id"]
                    group_uuids.append(group_uuid)
                    group_object = {
                        "uuid": group["id"],
                        "type": group["type"],
                        "asset_type": group["type"].split("--")[1],
                    }
                    if group := included.get(group_uuid):
                        attrs = group["attributes"]
                        rels = group["relationships"]
                        group_object.update(
                            {
                                "drupal_id": attrs["drupal_internal__id"],
                                "name": attrs["name"],
                                "is_location": attrs["is_location"],
                                "is_fixed": attrs["is_fixed"],
                                "archived": attrs["archived"],
                                "notes": attrs["notes"],
                            }
                        )
                    group_objects.append(group_object)

            if locations := relationships.get("location"):
                for location in locations["data"]:
                    location_uuid = location["id"]
                    location_uuids.append(location_uuid)
                    location_object = {
                        "uuid": location["id"],
                        "type": location["type"],
                        "asset_type": location["type"].split("--")[1],
                    }
                    if location := included.get(location_uuid):
                        attrs = location["attributes"]
                        rels = location["relationships"]
                        location_object.update(
                            {
                                "drupal_id": attrs["drupal_internal__id"],
                                "name": attrs["name"],
                                "is_location": attrs["is_location"],
                                "is_fixed": attrs["is_fixed"],
                                "archived": attrs["archived"],
                                "notes": attrs["notes"],
                            }
                        )
                    location_objects.append(location_object)

            if quantities := relationships.get("quantity"):
                for quantity in quantities["data"]:
                    quantity_uuid = quantity["id"]
                    quantity_uuids.append(quantity_uuid)
                    if quantity := included.get(quantity_uuid):
                        attrs = quantity["attributes"]
                        rels = quantity["relationships"]
                        value = attrs["value"]

                        unit_uuid = rels["units"]["data"]["id"]
                        unit = self.get_farmos_unit(unit_uuid)

                        measure_id = attrs["measure"]

                        quantity_object = {
                            "uuid": quantity["id"],
                            "drupal_id": attrs["drupal_internal__id"],
                            "quantity_type_uuid": rels["quantity_type"]["data"]["id"],
                            "quantity_type_id": rels["quantity_type"]["data"]["meta"][
                                "drupal_internal__target_id"
                            ],
                            "measure_id": measure_id,
                            "measure_name": self.get_farmos_measure_name(measure_id),
                            "value_numerator": value["numerator"],
                            "value_decimal": value["decimal"],
                            "value_denominator": value["denominator"],
                            "unit_uuid": unit_uuid,
                            "unit_name": unit["attributes"]["name"],
                        }
                        if quantity_object["quantity_type_id"] == "material":
                            quantity_object["material_types"] = [
                                {"uuid": mtype["id"]}
                                for mtype in quantity["relationships"]["material_type"][
                                    "data"
                                ]
                            ]
                        quantity_objects.append(quantity_object)

            if owners := relationships.get("owner"):
                for user in owners["data"]:
                    user_uuid = user["id"]
                    owner_uuids.append(user_uuid)
                    if user := included.get(user_uuid):
                        owner_objects.append(
                            {
                                "uuid": user["id"],
                                "name": user["attributes"]["name"],
                            }
                        )

        return {
            "uuid": log["id"],
            "drupal_id": log["attributes"]["drupal_internal__id"],
            "log_type_uuid": log_type_uuid,
            "log_type": log_type_object,
            "name": log["attributes"]["name"],
            "timestamp": timestamp,
            "assets": asset_objects,
            "groups": group_objects,
            "group_uuids": group_uuids,
            "quantities": quantity_objects,
            "quantity_uuids": quantity_uuids,
            "is_group_assignment": log["attributes"]["is_group_assignment"],
            "is_movement": log["attributes"]["is_movement"],
            "quick": log["attributes"]["quick"],
            "status": log["attributes"]["status"],
            "notes": notes,
            "locations": location_objects,
            "location_uuids": location_uuids,
            "owners": owner_objects,
            "owner_uuids": owner_uuids,
            # TODO: should we do this here or make caller do it?
            "vet": log["attributes"].get("vet"),
        }
