import re
from datetime import timedelta

from odoo import _, fields as odoo_fields, http
from odoo.http import request

from odoo.addons.portal.controllers.portal import CustomerPortal

_COLOR_RE = re.compile(r"^#[0-9a-fA-F]{6}$")
_VALID_POSITIONS = {"bottom-right", "bottom-left", "top-right", "top-left"}
_VALID_LANGS = {"de", "en"}
_VALID_PAGES = {"/", "/job-offers", "/company-profiles"}
_VALID_BTN_STYLES = {"pill", "rounded", "square"}
_VALID_RADII = {"16", "8", "0"}
_VALID_TOOLBAR_HEIGHTS = {"44", "56", "0"}
_VALID_BACKDROP = {"60", "40", "80", "0"}
_VALID_NAV = {"hidden", "left-sidebar"}
_VALID_BG_OPACITY = {"10", "20", "30", "50"}
_VALID_MODES = {"overlay", "inline", "grid", "box"}
_VALID_FONT_FAMILIES = {
    "system", "open-sans", "roboto", "lato", "montserrat", "raleway",
    "ubuntu", "merriweather", "playfair", "pt-sans", "noto-sans", "source-sans",
}
_VALID_NAVBAR_SCHEMES = {"default", "light", "dark", "custom"}
_VALID_FOOTER_SCHEMES = {"default", "light", "dark", "custom"}
_VALID_BOX_LAYOUTS = {"vertical", "horizontal"}
_VALID_BOX_TARGETS = {"_blank", "_parent"}
_VALID_FILTER_MODES = {"all", "curated", "filtered"}
_VALID_MENU_LAYOUTS = {"horizontal", "vertical"}


def _save_color(vals, kw, key):
    """Save a color field if present and valid."""
    v = kw.get(key, "").strip()
    if v and _COLOR_RE.match(v):
        vals[key] = v


def _save_enum(vals, kw, key, valid_set):
    """Save an enum field if value is in valid set."""
    v = kw.get(key, "").strip()
    if v in valid_set:
        vals[key] = v


def _save_text(vals, kw, key, max_len=200):
    """Save a text field, truncated."""
    v = kw.get(key, "").strip()
    if v:
        vals[key] = v[:max_len]


def _save_int(vals, kw, key, min_val=0, max_val=9999):
    """Save an integer field within range."""
    try:
        v = int(kw.get(key, ""))
        vals[key] = max(min_val, min(max_val, v))
    except (ValueError, TypeError):
        pass


def _save_bool(vals, kw, key):
    """Save a boolean field."""
    vals[key] = bool(kw.get(key))


def _save_css_size(vals, kw, key, max_len=50):
    """Save a CSS size value (e.g., '100%', '800px', 'auto')."""
    v = kw.get(key, "").strip()
    if v and re.match(r"^(\d+(%|px|vh|em|rem)|auto)$", v):
        vals[key] = v[:max_len]


class AffiliateWidgetPortal(CustomerPortal):

    def _prepare_home_portal_values(self, counters):
        values = super()._prepare_home_portal_values(counters)
        partner = request.env.user.partner_id
        if "widget_config_count" in counters and partner.role_user_type == "affiliate":
            values["widget_config_count"] = (
                request.env["affiliate.widget.config"]
                .search_count([("partner_id", "=", partner.id)])
            )
        return values

    def _get_or_create_widget_config(self):
        """Get existing config or auto-create one for the affiliate."""
        partner = request.env.user.partner_id
        config = request.env["affiliate.widget.config"].search(
            [("partner_id", "=", partner.id)], limit=1,
        )
        if not config:
            try:
                config = request.env["affiliate.widget.config"].sudo().create({
                    "partner_id": partner.id,
                })
                request.env.cr.flush()
            except Exception:
                request.env.cr.rollback()
                config = request.env["affiliate.widget.config"].search(
                    [("partner_id", "=", partner.id)], limit=1,
                )
        return config

    @http.route("/my/widget", type="http", auth="user", website=True)
    def portal_my_widget(self, **kw):
        """Affiliate widget configuration and embed code page."""
        partner = request.env.user.partner_id
        if partner.role_user_type != "affiliate":
            return request.redirect("/my")

        config = self._get_or_create_widget_config()
        embed_code = config.get_embed_code()

        date_30d = odoo_fields.Datetime.now() - timedelta(days=30)
        Event = request.env["affiliate.widget.event"]
        impressions_30d = Event.search_count([
            ("config_id", "=", config.id),
            ("event_type", "=", "impression"),
            ("event_date", ">=", date_30d),
        ])
        clicks_30d = Event.search_count([
            ("config_id", "=", config.id),
            ("event_type", "=", "click"),
            ("event_date", ">=", date_30d),
        ])

        values = self._prepare_portal_layout_values()
        values.update({
            "page_name": "widget",
            "config": config,
            "embed_code": embed_code,
            "impressions_30d": impressions_30d,
            "clicks_30d": clicks_30d,
            "ctr_30d": round((clicks_30d / impressions_30d * 100), 1) if impressions_30d else 0,
        })
        return request.render("kj_affiliate_widget.portal_my_widget", values)

    @http.route("/my/widget/save", type="http", auth="user", website=True, methods=["POST"])
    def portal_save_widget(self, **kw):
        """Save widget configuration changes."""
        partner = request.env.user.partner_id
        if partner.role_user_type != "affiliate":
            return request.redirect("/my")

        config = self._get_or_create_widget_config()
        vals = {}

        # --- Widget Mode ---
        _save_enum(vals, kw, "widget_mode", _VALID_MODES)
        _save_enum(vals, kw, "language", _VALID_LANGS)
        _save_enum(vals, kw, "landing_page", _VALID_PAGES)
        _save_color(vals, kw, "primary_color")

        # --- Shared Theme ---
        _save_color(vals, kw, "theme_color")
        _save_enum(vals, kw, "theme_font_family", _VALID_FONT_FAMILIES)
        _save_int(vals, kw, "theme_font_size", min_val=10, max_val=24)
        _save_color(vals, kw, "theme_bg_color")
        _save_color(vals, kw, "theme_heading_color")
        _save_color(vals, kw, "theme_text_color")
        _save_color(vals, kw, "theme_link_color")
        _save_color(vals, kw, "theme_btn_color")
        _save_color(vals, kw, "theme_border_color")
        _save_enum(vals, kw, "theme_navbar_scheme", _VALID_NAVBAR_SCHEMES)
        _save_color(vals, kw, "theme_navbar_bg")
        _save_color(vals, kw, "theme_navbar_text")
        _save_bool(vals, kw, "theme_site_footer_visible")
        _save_enum(vals, kw, "theme_site_footer_scheme", _VALID_FOOTER_SCHEMES)
        _save_color(vals, kw, "theme_site_footer_bg")
        _save_color(vals, kw, "theme_site_footer_text")
        _save_bool(vals, kw, "theme_show_search")
        _save_bool(vals, kw, "theme_show_logo")
        _save_bool(vals, kw, "theme_show_newsletter_popup")

        # --- Overlay-specific ---
        _save_enum(vals, kw, "position", _VALID_POSITIONS)
        _save_color(vals, kw, "text_color")
        _save_enum(vals, kw, "button_style", _VALID_BTN_STYLES)
        _save_enum(vals, kw, "overlay_radius", _VALID_RADII)
        _save_text(vals, kw, "button_text", max_len=100)
        _save_bool(vals, kw, "show_button")
        _save_bool(vals, kw, "show_logo")
        _save_enum(vals, kw, "toolbar_height", _VALID_TOOLBAR_HEIGHTS)
        _save_enum(vals, kw, "backdrop_opacity", _VALID_BACKDROP)
        _save_bool(vals, kw, "overlay_border")
        _save_color(vals, kw, "border_color")
        if kw.get("bg_image_url"):
            vals["bg_image_url"] = kw["bg_image_url"][:500]
        else:
            vals["bg_image_url"] = False
        _save_enum(vals, kw, "bg_image_opacity", _VALID_BG_OPACITY)
        _save_enum(vals, kw, "nav_layout", _VALID_NAV)
        _save_color(vals, kw, "nav_bg_color")
        _save_color(vals, kw, "nav_font_color")
        _save_bool(vals, kw, "show_footer")
        _save_text(vals, kw, "footer_text")
        _save_color(vals, kw, "footer_bg_color")
        _save_color(vals, kw, "footer_font_color")

        # --- Inline-specific ---
        _save_css_size(vals, kw, "inline_width")
        _save_css_size(vals, kw, "inline_height")
        _save_bool(vals, kw, "inline_auto_resize")
        _save_bool(vals, kw, "inline_show_header")
        _save_bool(vals, kw, "inline_show_topbar")
        _save_bool(vals, kw, "inline_show_logo")
        _save_text(vals, kw, "inline_custom_logo_url", max_len=500)
        _save_bool(vals, kw, "inline_show_menu")
        _save_enum(vals, kw, "inline_menu_layout", _VALID_MENU_LAYOUTS)
        _save_bool(vals, kw, "inline_show_menu_stellenangebote")
        _save_bool(vals, kw, "inline_show_menu_firmenprofile")
        _save_bool(vals, kw, "inline_show_menu_karriere_tipps")
        _save_bool(vals, kw, "inline_show_menu_actions")
        _save_enum(vals, kw, "inline_border_radius", _VALID_RADII)

        # Custom logo upload
        logo_file = kw.get("inline_custom_logo")
        if logo_file and hasattr(logo_file, "read"):
            import base64
            vals["inline_custom_logo"] = base64.b64encode(logo_file.read())
        elif kw.get("inline_custom_logo_clear") == "1":
            vals["inline_custom_logo"] = False

        # --- Box widget ---
        _save_text(vals, kw, "box_title", max_len=100)
        _save_int(vals, kw, "box_job_count", min_val=1, max_val=20)
        _save_enum(vals, kw, "box_layout", _VALID_BOX_LAYOUTS)
        _save_enum(vals, kw, "box_link_target", _VALID_BOX_TARGETS)
        _save_css_size(vals, kw, "box_width")
        _save_css_size(vals, kw, "box_height")
        _save_color(vals, kw, "box_header_bg")
        _save_color(vals, kw, "box_header_text")
        _save_int(vals, kw, "box_header_font_size", min_val=10, max_val=24)
        _save_color(vals, kw, "box_body_bg")
        _save_int(vals, kw, "box_body_font_size", min_val=10, max_val=24)
        _save_color(vals, kw, "box_link_color")
        _save_bool(vals, kw, "box_show_border")
        _save_color(vals, kw, "box_border_color")
        _save_color(vals, kw, "box_footer_bg")
        _save_color(vals, kw, "box_footer_text")

        if vals:
            config.sudo().write(vals)

        return request.redirect("/my/widget")
