from typing import TYPE_CHECKING
from shimoku.api.utils import change_data_set_name_with_report
from shimoku.code_gen.business_code_gen.apps_code_gen.data_sets_code_gen.code_gen_from_data_sets import (
    code_gen_read_csv_from_data_set,
)
from shimoku.code_gen.utils_code_gen import code_gen_from_dict, code_gen_from_list

if TYPE_CHECKING:
    from shimoku.api.resources.report import Report
    from shimoku.code_gen.business_code_gen.apps_code_gen.code_gen_from_apps import (
        AppCodeGen,
    )


def compact_labels_info(columns: list[dict], data_type_mapping: dict) -> dict:
    """Compact label info.
    :param columns: columns dict of the table
    :param data_type_mapping: mapping from field to data type
    :return: compacted label info
    """
    label_columns = {}
    for column in columns:
        if "chips" not in column:
            continue
        chips_dict = column["chips"]
        variant = chips_dict["variant"]
        mapping_from_field = data_type_mapping[column["field"]]
        entry_key = column["field"]
        chips_options = chips_dict["options"]
        if variant != "filled":
            entry_key = (entry_key, variant)
        entry_value = {}
        if mapping_from_field.startswith("intField"):
            chips_options = sorted(chips_options, key=lambda x: int(x["value"]))
            current_range = (0, chips_options[0]["value"])
            current_color = chips_options[0]["backgroundColor"]
            first_index = 0
            i = 1
            for chip in chips_options[1:]:
                if chip["backgroundColor"] != current_color:
                    if i == first_index + 1:
                        entry_value[current_range[1]] = current_color
                    else:
                        entry_value[current_range] = current_color
                    first_index = i
                    current_range = (current_range[1], chip["value"])
                    current_color = chip["backgroundColor"]
                else:
                    current_range = (current_range[0], chip["value"])
                i += 1
            if first_index == len(chips_options) - 1:
                entry_value[current_range[1]] = current_color
            else:
                entry_value[
                    (current_range[0], int(current_range[1]) + 1)
                ] = current_color
        else:
            entry_value = {
                chip["value"]: chip["backgroundColor"] for chip in chips_options
            }
        first_color = chips_options[0]["backgroundColor"]
        if all(v == first_color for v in entry_value.values()):
            entry_value = first_color
        label_columns[entry_key] = entry_value
    return label_columns


def get_column_options(columns: list[dict]) -> dict:
    """Get column options.
    :return: column options
    """
    column_options = {}
    for column in columns:
        not_wanted_keys = ["field", "headerName", "order", "type", "chips", "options"]
        single_column_options = {
            k: v for k, v in column.items() if k not in not_wanted_keys
        }
        if single_column_options:
            column_options[column["field"]] = single_column_options
    return column_options


async def code_gen_from_table(
    self: "AppCodeGen", report: "Report", properties: dict
) -> list[str]:
    """Generate code for a table report.
    :param report: report to generate code from
    :param properties: properties of the report
    :return: list of code lines
    """
    report_data_set: Report.ReportDataSet = (await report.get_report_data_sets())[0]
    data_set_id = report_data_set["dataSetId"]
    data_set = await self._app.get_data_set(data_set_id)
    data_arg = await code_gen_read_csv_from_data_set(
        data_set, change_data_set_name_with_report(data_set, report)
    )
    if data_arg is None:
        return ["pass"]
    if data_set_id in self._code_gen_tree.shared_data_sets:
        data_arg = f'"{data_set["name"]}",'
    table_params = []
    rev_mapping = {
        v: (k if data_set["columns"] else v)
        for k, v in properties["rows"]["mapping"].items()
    }
    mapping = properties["rows"]["mapping"]
    table_params.append(f"    columns={list(rev_mapping.values())},")
    if properties["pagination"]["pageSize"] != 10:
        table_params.append(f'    page_size={properties["pagination"]["pageSize"]},')
    if "pageSizeOptions" in properties["pagination"]:
        page_size_code_lines = code_gen_from_list(
            properties["pagination"]["pageSizeOptions"], 8
        )
        table_params.extend(
            [
                f"    page_size_options={page_size_code_lines[0][8:]}",
                *page_size_code_lines[1:],
            ]
        )
    if not properties["columnsButton"]:
        table_params.append("    columns_button=False,")
    if not properties["filtersButton"]:
        table_params.append("    filters=False,")
    if not properties["exportButton"]:
        table_params.append("    export_to_csv=False,")
    if not properties["search"]:
        table_params.append("    search=False,")
    if properties.get("sort"):
        sort_field = rev_mapping[properties["sort"]["field"]]
        sort_direction = properties["sort"]["direction"]
        table_params.append(f'    initial_sort_column="{sort_field}",')
        if sort_direction != "asc":
            table_params.append("    sort_descending=True,")
    column_options = get_column_options(properties["columns"])
    if column_options:
        column_options_code_lines = code_gen_from_dict(column_options, 8)
        table_params.extend(
            [
                f"    columns_options={column_options_code_lines[0][8:]}",
                *column_options_code_lines[1:],
            ]
        )

    categorical_columns = [
        col_dict["field"] if data_set["columns"] else mapping[col_dict["field"]]
        for col_dict in properties["columns"]
        if col_dict.get("type") == "singleSelect"
    ]
    if categorical_columns:
        table_params.append(f"    categorical_columns={categorical_columns},")
    label_columns = compact_labels_info(properties["columns"], mapping)
    if label_columns:
        label_columns_code_lines = code_gen_from_dict(label_columns, 4)
        table_params.extend(
            [
                f"    label_columns={label_columns_code_lines[0][4:]}",
                *label_columns_code_lines[1:],
            ]
        )
    return [
        "shimoku_client.plt.table(",
        f"    data={data_arg},",
        *self.code_gen_report_params(report.cascade_to_dict()),
        *table_params,
        ")",
    ]
