from dataclasses import asdict
from typing import Literal

import mesop as me

ROW_GAP = 15
BOX_PADDING = 20


@me.stateclass
class State:
  first_name: str
  last_name: str
  username: str
  email: str
  address: str
  address_2: str
  country: str
  state: str
  zip: str
  payment_type: str
  name_on_card: str
  credit_card: str
  expiration: str
  cvv: str
  errors: dict[str, str]


def is_mobile():
  return me.viewport_size().width < 620


def calc_input_size(items: int):
  return int(
    (me.viewport_size().width - (ROW_GAP * items) - (BOX_PADDING * 2)) / items
  )


def load(e: me.LoadEvent):
  me.set_theme_density(-3)
  me.set_theme_mode("system")


@me.page(
  security_policy=me.SecurityPolicy(
    allowed_iframe_parents=["https://google.github.io", "https://huggingface.co"]
  ),
  path="/form_billing",
  on_load=load,
)
def page():
  state = me.state(State)

  with me.box(
    style=me.Style(
      padding=me.Padding.all(BOX_PADDING),
      max_width=800,
      margin=me.Margin.symmetric(horizontal="auto"),
    )
  ):
    me.text(
      "Billing form",
      type="headline-4",
      style=me.Style(margin=me.Margin(bottom=10)),
    )

    with form_group():
      name_width = calc_input_size(2) if is_mobile() else "100%"
      input_field(label="First name", width=name_width)
      input_field(label="Last name", width=name_width)

    with form_group():
      input_field(label="Username")

    with me.box(style=me.Style(display="flex", gap=ROW_GAP)):
      input_field(label="Email", input_type="email")

    with me.box(style=me.Style(display="flex", gap=ROW_GAP)):
      input_field(label="Address")

    with form_group():
      input_field(label="Address 2")

    with form_group():
      country_state_zip_width = calc_input_size(3) if is_mobile() else "100%"
      input_field(label="Country", width=country_state_zip_width)
      input_field(label="State", width=country_state_zip_width)
      input_field(label="Zip", width=country_state_zip_width)

    divider()

    me.text(
      "Payment",
      type="headline-4",
      style=me.Style(margin=me.Margin(bottom=10)),
    )

    with form_group(flex_direction="column"):
      me.radio(
        key="payment_type",
        on_change=on_change,
        options=[
          me.RadioOption(label="Credit card", value="credit_card"),
          me.RadioOption(label="Debit card", value="debit_card"),
          me.RadioOption(label="Paypal", value="paypal"),
        ],
        style=me.Style(
          display="flex", flex_direction="column", margin=me.Margin(bottom=20)
        ),
      )
      if "payment_type" in state.errors:
        me.text(
          state.errors["payment_type"],
          style=me.Style(
            margin=me.Margin(top=-30, left=5, bottom=15),
            color=me.theme_var("error"),
            font_size=13,
          ),
        )

    with form_group():
      payments_width = calc_input_size(2) if is_mobile() else "100%"
      input_field(label="Name on card", width=payments_width)
      input_field(label="Credit card", width=payments_width)

    with form_group():
      input_field(label="Expiration", width=payments_width)
      input_field(label="CVV", width=payments_width, input_type="number")

    divider()

    me.button(
      "Continue to checkout",
      type="flat",
      style=me.Style(width="100%", padding=me.Padding.all(25), font_size=20),
      on_click=on_click,
    )


def divider():
  with me.box(style=me.Style(margin=me.Margin.symmetric(vertical=20))):
    me.divider()


@me.content_component
def form_group(flex_direction: Literal["row", "column"] = "row"):
  with me.box(
    style=me.Style(
      display="flex", flex_direction=flex_direction, gap=ROW_GAP, width="100%"
    )
  ):
    me.slot()


def input_field(
  *,
  key: str = "",
  label: str,
  value: str = "",
  width: str | int = "100%",
  input_type: Literal[
    "color",
    "date",
    "datetime-local",
    "email",
    "month",
    "number",
    "password",
    "search",
    "tel",
    "text",
    "time",
    "url",
    "week",
  ] = "text",
):
  state = me.state(State)
  key = key if key else label.lower().replace(" ", "_")
  with me.box(style=me.Style(flex_grow=1, width=width)):
    me.input(
      key=key,
      label=label,
      value=value,
      appearance="outline",
      color="warn" if key in state.errors else "primary",
      style=me.Style(width=width),
      type=input_type,
      on_blur=on_blur,
    )
    if key in state.errors:
      me.text(
        state.errors[key],
        style=me.Style(
          margin=me.Margin(top=-13, left=15, bottom=15),
          color=me.theme_var("error"),
          font_size=13,
        ),
      )


def on_change(e: me.RadioChangeEvent):
  state = me.state(State)
  setattr(state, e.key, e.value)


def on_blur(e: me.InputBlurEvent):
  state = me.state(State)
  setattr(state, e.key, e.value)


def on_click(e: me.ClickEvent):
  state = me.state(State)

  # Replace with real validation logic.
  errors = {}
  for key, value in asdict(state).items():  # type: ignore
    if key == "error":
      continue
    if not value:
      errors[key] = f"{key.replace('_', ' ').capitalize()} is required"
  state.errors = errors

  # Replace with form processing logic.
  if not state.errors:
    pass