import json
import urllib.parse

from fastapi import Request
from folkugat_web.model import temes as model
from folkugat_web.model.lilypond.processing import RenderError
from folkugat_web.model.pagines import Pages
from folkugat_web.model.search import Order, OrderBy, OrderParams
from folkugat_web.services import sessions as sessions_service
from folkugat_web.services.sessions import get_date_names
from folkugat_web.services.temes import query as temes_q
from folkugat_web.services.temes import scores as scores_service
from folkugat_web.services.temes import search as temes_s
from folkugat_web.templates import templates
from folkugat_web.utils import FnChain


def build_temes_params(
    query: str,
    properties: list[str],
    order_params: OrderParams | None = None,
) -> str:
    content_params = [
        ("query", query),
        *[("properties", prop) for prop in properties or []]
    ]
    if order_params:
        content_params.append(("order_by", order_params.order_by.value))
        content_params.append(("order", order_params.order.value))
    return urllib.parse.urlencode(content_params)


def build_hx_vars(*vars_dicts:  dict[str, str]) -> str:
    result = ",".join(f"{var}:{value}" for hx_vars in vars_dicts for var, value in hx_vars.items())
    return result


def build_property_str(properties: list[str]) -> str:
    return json.dumps(properties).replace("'", "\\'").replace('"', "'")


def build_order_params_dict(order_params: OrderParams | None) -> dict[str, str]:
    if not order_params:
        return {}
    return {
        "order_by": f"'{order_params.order_by.value}'",
        "order": f"'{order_params.order.value}'",
    }


def temes_pagina(
    request: Request,
    logged_in: bool,
    query: str,
    properties: list[str],
    order_params: OrderParams | None,
):
    properties_str = build_property_str(properties)
    order_params_dict = build_order_params_dict(order_params)
    return templates.TemplateResponse(
        "fragments/temes/pagina.html",
        {
            "request": request,
            "logged_in": logged_in,
            "query": query,
            "properties": properties,
            "properties_str": properties_str,
            "order_params": order_params,
            "order_params_dict": order_params_dict,
            "build_hx_vars": build_hx_vars,
            "property_results": [],
            "Order": Order,
            "OrderBy": OrderBy,
            "Pages": Pages,
            "get_date_names": get_date_names,
            "menu_selected_id": Pages.Temes,
        }
    )


def temes_busca(
    request: Request,
    logged_in: bool,
    query: str,
    properties: list[str],
    order_params: OrderParams | None = None,
    offset: int = 0,
    limit: int = 10,
):
    temes = temes_s.busca_temes(
        query=query,
        properties=properties,
        order_params=order_params,
        hidden=logged_in,
        limit=limit + 1,
        offset=offset,
    )

    next_offset, prev_offset = None, None
    if len(temes) > limit:
        next_offset = offset + limit
        temes = temes[:limit]
    if offset > 0:
        prev_offset = max(offset - limit, 0)

    temes = (
        FnChain.transform(temes) |
        temes_q.temes_compute_stats |
        scores_service.add_scores_to_temes |
        # No properties added because search already does that
        list
    ).result()

    property_results = [
        prop for prop in temes_s.busca_properties(query=query, limit=5)
        if prop not in properties
    ]

    temes_params = build_temes_params(
        query=query,
        properties=properties,
        order_params=order_params,
    )
    temes_url = f"/temes?{temes_params}"

    def _add_property_str(prop: str) -> str:
        properties_dedup = list(set(properties + [prop]))
        return build_property_str(properties_dedup)

    def _remove_property_str(prop: str) -> str:
        properties_clean = [p for p in properties if p.lower() != prop.lower()]
        return build_property_str(properties_clean)

    return templates.TemplateResponse(
        "fragments/temes/results.html",
        {
            "request": request,
            "logged_in": logged_in,
            "temes": temes,
            "query": query,
            "properties": properties,
            "properties_str": build_property_str(properties),
            "order_params": order_params,
            "order_params_dict": build_order_params_dict(order_params),
            "property_results": property_results,
            "prev_offset": prev_offset,
            "next_offset": next_offset,
            "LinkType": model.LinkType,
            "ContentType": model.ContentType,
            "Order": Order,
            "OrderBy": OrderBy,
            "get_date_names": get_date_names,
            "add_property_str": _add_property_str,
            "remove_property_str": _remove_property_str,
            "build_hx_vars": build_hx_vars,
        },
        headers={"HX-Replace-Url": temes_url},
    )


def tema(request: Request, logged_in: bool, tema: model.Tema):
    return templates.TemplateResponse(
        "fragments/tema/pagina.html",
        {
            "request": request,
            "logged_in": logged_in,
            "Pages": Pages,
            "LinkType": model.LinkType,
            "ContentType": model.ContentType,
            "tema": tema,
            "date_names": sessions_service.get_date_names,
        }
    )


def score_render(
    request: Request,
    score_id: int,
    score_render_url: str = "",
    errors: list[RenderError] | None = None,
):
    return templates.TemplateResponse(
        "fragments/tema/editor/score_render.html",
        {
            "request": request,
            "score_id": score_id,
            "score_render_url": score_render_url,
            "errors": errors,
        }
    )
