# -*- coding: utf-8 -*-
"""SAM Wind Balance of System Cost Model"""

from copy import deepcopy

import numpy as np
from PySAM.PySSC import ssc_sim_from_dict

from reV.utilities.exceptions import SAMInputError


class WindBos:
    """Wind Balance of System Cost Model."""

    MODULE = "windbos"

    # keys for the windbos input data dictionary.
    # Some keys may not be found explicitly in the SAM input.
    KEYS = (
        "tech_model",
        "financial_model",
        "machine_rating",
        "rotor_diameter",
        "hub_height",
        "number_of_turbines",
        "interconnect_voltage",
        "distance_to_interconnect",
        "site_terrain",
        "turbine_layout",
        "soil_condition",
        "construction_time",
        "om_building_size",
        "quantity_test_met_towers",
        "quantity_permanent_met_towers",
        "weather_delay_days",
        "crane_breakdowns",
        "access_road_entrances",
        "turbine_capital_cost",
        "turbine_cost_per_kw",
        "tower_top_mass",
        "delivery_assist_required",
        "pad_mount_transformer_required",
        "new_switchyard_required",
        "rock_trenching_required",
        "mv_thermal_backfill",
        "mv_overhead_collector",
        "performance_bond",
        "contingency",
        "warranty_management",
        "sales_and_use_tax",
        "overhead",
        "profit_margin",
        "development_fee",
        "turbine_transportation",
    )

    def __init__(self, inputs):
        """
        Parameters
        ----------
        inputs : dict
            SAM key value pair inputs.
        """

        self._turbine_capital_cost = 0.0
        self._datadict = {}

        self._inputs = inputs
        self._special = {
            "tech_model": "windbos",
            "financial_model": "none",
            "machine_rating": self.machine_rating,
            "hub_height": self.hub_height,
            "rotor_diameter": self.rotor_diameter,
            "number_of_turbines": self.number_of_turbines,
            "turbine_capital_cost": self.turbine_capital_cost,
        }
        self._parse_inputs()
        self._out = ssc_sim_from_dict(self._datadict)

    def _parse_inputs(self):
        """Parse SAM inputs into a windbos input dict and perform any
        required special operations."""

        for k in self.KEYS:
            if k in self._special:
                self._datadict[k] = self._special[k]
            elif k not in self._inputs:
                raise SAMInputError(
                    'Windbos requires input key: "{}"'.format(k)
                )
            else:
                self._datadict[k] = self._inputs[k]

    @property
    def machine_rating(self):
        """Single turbine machine rating either from input or power curve."""
        if "machine_rating" in self._inputs:
            return self._inputs["machine_rating"]
        else:
            return np.max(self._inputs["wind_turbine_powercurve_powerout"])

    @property
    def hub_height(self):
        """Turbine hub height."""
        if "wind_turbine_hub_ht" in self._inputs:
            return self._inputs["wind_turbine_hub_ht"]
        else:
            return self._inputs["hub_height"]

    @property
    def rotor_diameter(self):
        """Turbine rotor diameter."""
        if "wind_turbine_rotor_diameter" in self._inputs:
            return self._inputs["wind_turbine_rotor_diameter"]
        else:
            return self._inputs["rotor_diameter"]

    @property
    def number_of_turbines(self):
        """Number of turbines either based on input or system (farm) capacity
        and machine rating"""

        if "number_of_turbines" in self._inputs:
            return self._inputs["number_of_turbines"]
        else:
            return (
                self._inputs['system_capacity'] / self.machine_rating
            )

    @property
    def turbine_capital_cost(self):
        """Returns zero (no turbine capital cost for WindBOS input,
        and assigns any input turbine_capital_cost to an attr"""

        if "turbine_capital_cost" in self._inputs:
            self._turbine_capital_cost = self._inputs["turbine_capital_cost"]
        else:
            self._turbine_capital_cost = 0.0
        return 0.0

    @property
    def bos_cost(self):
        """Get the balance of system cost ($)."""
        return self._out["project_total_budgeted_cost"]

    @property
    def turbine_cost(self):
        """Get the turbine cost ($)."""
        tcost = (
            self._inputs["turbine_cost_per_kw"]
            * self.machine_rating
            * self.number_of_turbines
        ) + (self._turbine_capital_cost * self.number_of_turbines)
        return tcost

    @property
    def sales_tax_mult(self):
        """Get a sales tax multiplier (frac of the total installed cost)."""
        basis = self._inputs.get("sales_tax_basis", 0) / 100
        tax = self._datadict.get("sales_and_use_tax", 0) / 100
        return basis * tax

    @property
    def sales_tax_cost(self):
        """Get the cost of sales tax ($)."""
        return (self.bos_cost + self.turbine_cost) * self.sales_tax_mult

    @property
    def total_installed_cost(self):
        """Get the total installed cost ($) (bos + turbine)."""
        return self.bos_cost + self.turbine_cost + self.sales_tax_cost

    @property
    def output(self):
        """Get a dictionary containing the cost breakdown."""
        output = {
            "total_installed_cost": self.total_installed_cost,
            "turbine_cost": self.turbine_cost,
            "sales_tax_cost": self.sales_tax_cost,
            "bos_cost": self.bos_cost,
        }
        return output

    # pylint: disable-msg=W0613
    @classmethod
    def reV_run(
        cls,
        points_control,
        site_df,
        output_request=("total_installed_cost",),
        **kwargs,
    ):
        """Execute SAM SingleOwner simulations based on reV points control.

        Parameters
        ----------
        points_control : config.PointsControl
            PointsControl instance containing project points site and SAM
            config info.
        site_df : pd.DataFrame
            Dataframe of site-specific input variables. Row index corresponds
            to site number/gid (via df.loc not df.iloc), column labels are the
            variable keys that will be passed forward as SAM parameters.
        output_request : list | tuple | str
            Output(s) to retrieve from SAM.
        kwargs : dict
            Not used but maintained for polymorphic calls with other
            SAM econ reV_run() methods (lcoe and single owner).
            Breaks pylint error W0613: unused argument.

        Returns
        -------
        out : dict
            Nested dictionaries where the top level key is the site index,
            the second level key is the variable name, second level value is
            the output variable value.
        """
        out = {}

        for site in points_control.sites:
            # get SAM inputs from project_points based on the current site
            _, inputs = points_control.project_points[site]

            # ensure that site-specific data is not persisted to other sites
            site_inputs = deepcopy(inputs)

            site_inputs.update(dict(site_df.loc[site, :]))

            wb = cls(site_inputs)

            out[site] = {
                k: v for k, v in wb.output.items() if k in output_request
            }

        return out
