from typing import Optional, Union, TYPE_CHECKING
from math import floor, ceil, log10
from pandas import DataFrame
from copy import deepcopy

from shimoku.plt.utils import validate_data_is_pandarable, ShimokuPalette
from shimoku.plt.EChart_definitions.default_echart_options import (
    get_common_echart_options,
    get_common_series_options,
)

if TYPE_CHECKING:
    from shimoku.plt.plt_layer import PlotLayer


async def line_and_bar_charts(
    self: "PlotLayer",
    order: int,
    x: str,
    data: Optional[Union[str, DataFrame, list[dict]]],
    bar_names: Optional[list[str]] = None,
    line_names: Optional[list[str]] = None,
    x_axis_name: Optional[str] = None,
    bar_axis_name: Optional[str] = None,
    bar_suffix: Optional[str] = None,
    line_axis_name: Optional[str] = None,
    line_suffix: Optional[str] = None,
    title: Optional[str] = None,
    rows_size: Optional[int] = None,
    cols_size: Optional[int] = None,
    padding: Optional[str] = None,
    option_modifications: Optional[dict] = None,
    variant: Optional[str] = None,
):
    """Create a chart with bars and lines."""
    data_mappings_to_tuples = await self._choose_data(order, data)
    if isinstance(data, str):
        data = self._shared_data[data]

    if bar_names is None:
        bar_names = ["bar"]

    if line_names is None:
        line_names = ["line"]

    df: DataFrame = validate_data_is_pandarable(data)
    max_bars = df[bar_names].max().max()
    pow_10 = floor(log10(abs(max_bars)))
    max_bars = ceil(max_bars / 10**pow_10) * 10**pow_10
    bar_interval = abs(max_bars) / (5 if 1000 > max_bars > 0.01 else 10)

    longest_value_bars = max(len(str(max_bars)), len(str(bar_interval)))

    max_lines = df[line_names].max().max()
    pow_10 = floor(log10(abs(max_lines)))
    max_lines = ceil(max_lines / 10**pow_10) * 10**pow_10
    line_interval = abs(max_lines) / (5 if 1000 > max_lines > 0.01 else 10)

    longest_value_lines = max(len(str(max_lines)), len(str(line_interval)))

    common_options = get_common_echart_options()
    x_axis = common_options["xAxis"][0]
    del x_axis["data"]
    x_axis["type"] = "value"
    x_axis["scale"] = True
    common_options["yAxis"][0]["scale"] = True

    common_options.update(
        {
            "xAxis": [
                {
                    "data": "#set_data#",
                    "fontFamily": "Rubik",
                    "axisPointer": {"type": "shadow"},
                    "type": "category",
                    "nameLocation": "middle",
                    "nameGap": 35,
                }
            ],
            "yAxis": [
                {
                    "type": "value",
                    "fontFamily": "Rubik",
                    "axisLabel": {"formatter": "{value}" + bar_suffix},
                    "nameLocation": "middle",
                    "nameGap": 24 + 7 * (longest_value_bars + int(len(bar_suffix))),
                    "alignTicks": True,
                    "axisLine": {
                        "show": True,
                        "lineStyle": {"color": ShimokuPalette.CHART_C1.value},
                    },
                    "splitNumber": 5,
                    "interval": bar_interval,
                    "max": max_bars,
                },
                {
                    "type": "value",
                    "fontFamily": "Rubik",
                    "axisLabel": {"formatter": "{value}" + line_suffix},
                    "nameLocation": "middle",
                    "nameGap": 24 + 7 * (longest_value_lines + int(len(line_suffix))),
                    "axisLine": {
                        "show": True,
                        "lineStyle": {"color": f"var(--chart-C{len(bar_names)+2})"},
                    },
                    "splitNumber": 5,
                    "interval": line_interval,
                    "max": max_lines,
                },
            ],
        }
    )
    series_options = get_common_series_options()

    series = []
    for index, name in enumerate(bar_names):
        sop = deepcopy(series_options)
        sop.update(
            {
                "name": name,
                "type": "bar",
                "itemStyle": {
                    "color": f"var(--chart-C{index+1})",
                    "borderRadius": [9, 9, 0, 0],
                },
                "emphasis": {
                    "itemStyle": {
                        "color": f"var(--chart-C{index+1})",
                        "borderRadius": [9, 9, 0, 0],
                    },
                    "lineStyle": {
                        "color": f"var(--chart-C{index+1})",
                    },
                },
                "smooth": True,
            }
        )
        series.append(sop)

    for index, name in enumerate(line_names):
        sop = deepcopy(series_options)
        sop.update(
            {
                "name": name,
                "type": "line",
                "yAxisIndex": 1,
                "itemStyle": {
                    "color": f"var(--chart-C{len(bar_names) + index + 1})",
                    "borderRadius": [9, 9, 0, 0],
                },
                "emphasis": {
                    "itemStyle": {
                        "color": f"var(--chart-C{len(bar_names) + index + 1})",
                        "borderRadius": [9, 9, 0, 0],
                    },
                    "lineStyle": {
                        "color": f"var(--chart-C{len(bar_names) + index + 1})",
                    },
                },
                "smooth": True,
            }
        )
        series.append(sop)

    await self._create_trend_chart(
        echart_options=common_options,
        series_options=series,
        axes=x,
        values=bar_names + line_names,
        option_modifications=option_modifications,
        data_mapping_to_tuples=data_mappings_to_tuples,
        order=order,
        x_axis_names=x_axis_name,
        y_axis_names=[bar_axis_name, line_axis_name],
        variant=variant,
        title=title,
        rows_size=rows_size,
        cols_size=cols_size,
        padding=padding,
    )
