#
# Copyright (c) Daniel Sheffield 2023
#
# All rights reserved
#
# THIS SOFTWARE IS PROVIDED AS IS WITHOUT WARRANTY
from typing import Dict, Tuple
from bottle import template
from pandas import DataFrame
from itertools import chain
from ..rest import ALL_UNITS

def get_option_groups(
    data: DataFrame, filter_data: Dict[str, Tuple[set, set]],
    k: str, g: str, _type: str
):
    in_chart = data['$/unit'].apply(lambda x: (x or False) and True)
    groups = sorted(set(data[g] if g is not None else []))
    groups.append(None)
    if _type == "exclude":
        prefix = "!"
    else:
        prefix = ""
    
    for group in groups:
        selected = []
        unselected = []
        if group is None:
            if _type == "include":
                selected.extend(filter_data[k][0] - (
                    set((chain(*data[in_chart]["tags"]) if k == "tag" else data[in_chart][k]) if g is not None else set())
                ))
                unselected.extend(((
                    set((chain(*data[in_chart]["tags"]) if k == "tag" else data[in_chart][k]) if g is None else set())
                ) | filter_data[k][1]) - filter_data[k][0])
            else:
                selected.extend(filter_data[k][1])
                unselected.extend((
                    set((chain(*data[in_chart]["tags"]) if k == "tag" else data[in_chart][k]) if g is None else set())
                ) | (
                    filter_data[k][0] - set(chain(*data[in_chart]["tags"]) if k == "tag" else data[in_chart][k]) - filter_data[k][1]
                ))
        else:
            if _type == "include":
                selected.extend(filter_data[k][0] & set(
                    data[in_chart & (data[g].apply(lambda x,axis=None: x == group))][k]
                ))
                unselected.extend(set(
                    data[in_chart & (data[g].apply(lambda x,axis=None: x == group))][k]
                ) - filter_data[k][0])
            else:
                unselected.extend(set(
                    data[in_chart & (data[g].apply(lambda x,axis=None: x == group))][k]
                ))
        assert set(selected) - set(unselected) == set(selected), f"{set(selected)} {set(unselected)}"
        
        yield {
            "optgroup": group,
            "options": sorted(map(lambda x: {
                "selected": x[0],
                "value": f"{prefix}{x[1]}",
                "display": x[1]  
            }, chain(
                map(lambda x: (True, x), set(selected)),
                map(lambda x: (False, x), set(unselected)),
            )), key=lambda x: x["display"] if "display" in x else x["value"])
        }


def get_form(action: str, method: str, filter_data, data: DataFrame):
    keys = sorted(filter(lambda x: x not in ('unit', 'tag'), filter_data), key=lambda x: {
        'product': 0,
        'category': 1,
        'group': 2,
    }[x])
    return template('form-filter', action=action, method=method,
        params=filter_data,
        **{
            k: {
                "name": k,
                "_include": {
                    "option_groups": get_option_groups(data, filter_data, k, g, "include"),
                },
                "_exclude": {
                    "option_groups": get_option_groups(data, filter_data, k, g, "exclude"),
                }
            } for k, g in zip(keys, [*keys[1:], None])
        },
        tags={
            "name": "tag",
            "_include": {
                "option_groups": get_option_groups(data, filter_data, "tag", None, "include")
            },
            "_exclude": {
                "option_groups": get_option_groups(data, filter_data, "tag", None, "exclude")
            },
        },
        units={
            "name": "unit",
            "options": sorted(map(lambda x: {
                "selected": x[0],
                "value": x[1],
            },chain(
                map(lambda x: (True, x), filter_data['unit'][0]),
                map(lambda x: (False, x), ALL_UNITS - filter_data['unit'][0])
            )), key=lambda x: x["display"] if "display" in x else x["value"])
        }
    )
