package de.fete.adapter.in.web;

import de.fete.domain.model.Event;
import de.fete.domain.model.EventToken;
import de.fete.domain.port.in.GetEventUseCase;
import jakarta.annotation.PostConstruct;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

/** Serves the SPA index.html with injected Open Graph and Twitter Card meta-tags. */
@Controller
public class SpaController {

  private static final String PLACEHOLDER = "<!-- OG_META_TAGS -->";
  private static final int MAX_TITLE_LENGTH = 70;
  private static final int MAX_DESCRIPTION_LENGTH = 200;
  private static final String GENERIC_TITLE = "fete";
  private static final String GENERIC_DESCRIPTION =
      "Privacy-focused event planning. Create and share events without accounts.";
  private static final DateTimeFormatter DATE_FORMAT =
      DateTimeFormatter.ofPattern("EEEE, MMMM d, yyyy 'at' h:mm a", Locale.ENGLISH);

  private final GetEventUseCase getEventUseCase;
  private String htmlTemplate;

  /** Creates a new SpaController. */
  public SpaController(GetEventUseCase getEventUseCase) {
    this.getEventUseCase = getEventUseCase;
  }

  /** Loads and caches the index.html template at startup. */
  @PostConstruct
  void loadTemplate() throws IOException {
    var resource = new ClassPathResource("/static/index.html");
    if (resource.exists()) {
      htmlTemplate = resource.getContentAsString(StandardCharsets.UTF_8);
    }
  }

  /** Serves SPA HTML with generic meta-tags for non-event routes. */
  @GetMapping(
      value = {"/", "/create", "/events"},
      produces = MediaType.TEXT_HTML_VALUE
  )
  @ResponseBody
  public String serveGenericPage(HttpServletRequest request) {
    if (htmlTemplate == null) {
      return "";
    }
    String baseUrl = getBaseUrl(request);
    return htmlTemplate.replace(PLACEHOLDER, renderTags(buildGenericMeta(baseUrl)));
  }

  /** Serves SPA HTML with event-specific meta-tags. */
  @GetMapping(
      value = "/events/{eventToken}",
      produces = MediaType.TEXT_HTML_VALUE
  )
  @ResponseBody
  public String serveEventPage(@PathVariable String eventToken,
      HttpServletRequest request) {
    if (htmlTemplate == null) {
      return "";
    }
    String baseUrl = getBaseUrl(request);
    Map<String, String> meta = resolveEventMeta(eventToken, baseUrl);
    return htmlTemplate.replace(PLACEHOLDER, renderTags(meta));
  }

  // --- Meta-tag composition ---

  private Map<String, String> buildEventMeta(Event event, String baseUrl) {
    var tags = new LinkedHashMap<String, String>();
    String title = truncateTitle(event.title());
    String description = formatDescription(event);
    tags.put("og:title", title);
    tags.put("og:description", description);
    tags.put("og:url", baseUrl + "/events/" + event.eventToken().value());
    tags.put("og:type", "website");
    tags.put("og:site_name", GENERIC_TITLE);
    tags.put("og:image", baseUrl + "/og-image.png");
    tags.put("twitter:card", "summary");
    tags.put("twitter:title", title);
    tags.put("twitter:description", description);
    return tags;
  }

  private Map<String, String> buildGenericMeta(String baseUrl) {
    var tags = new LinkedHashMap<String, String>();
    tags.put("og:title", GENERIC_TITLE);
    tags.put("og:description", GENERIC_DESCRIPTION);
    tags.put("og:url", baseUrl);
    tags.put("og:type", "website");
    tags.put("og:site_name", GENERIC_TITLE);
    tags.put("og:image", baseUrl + "/og-image.png");
    tags.put("twitter:card", "summary");
    tags.put("twitter:title", GENERIC_TITLE);
    tags.put("twitter:description", GENERIC_DESCRIPTION);
    return tags;
  }

  private Map<String, String> resolveEventMeta(String token, String baseUrl) {
    try {
      UUID uuid = UUID.fromString(token);
      Optional<Event> event =
          getEventUseCase.getByEventToken(new EventToken(uuid));
      if (event.isPresent()) {
        return buildEventMeta(event.get(), baseUrl);
      }
    } catch (IllegalArgumentException ignored) {
      // Invalid UUID — fall back to generic
    }
    return buildGenericMeta(baseUrl);
  }

  // --- Description formatting ---

  private String truncateTitle(String title) {
    if (title.length() <= MAX_TITLE_LENGTH) {
      return title;
    }
    return title.substring(0, MAX_TITLE_LENGTH - 3) + "...";
  }

  private String formatDescription(Event event) {
    ZonedDateTime zoned = event.dateTime().atZoneSameInstant(event.timezone());
    var sb = new StringBuilder();
    sb.append("📅 ").append(zoned.format(DATE_FORMAT));

    if (event.location() != null && !event.location().isBlank()) {
      sb.append(" · 📍 ").append(event.location());
    }

    if (event.description() != null && !event.description().isBlank()) {
      sb.append(" — ").append(event.description());
    }

    String result = sb.toString();
    if (result.length() > MAX_DESCRIPTION_LENGTH) {
      return result.substring(0, MAX_DESCRIPTION_LENGTH - 3) + "...";
    }
    return result;
  }

  // --- HTML rendering ---

  private String renderTags(Map<String, String> tags) {
    var sb = new StringBuilder();
    for (var entry : tags.entrySet()) {
      String key = entry.getKey();
      String value = escapeHtml(entry.getValue());
      String attr = key.startsWith("twitter:") ? "name" : "property";
      sb.append("<meta ").append(attr).append("=\"").append(key)
          .append("\" content=\"").append(value).append("\">\n");
    }
    return sb.toString().stripTrailing();
  }

  private String escapeHtml(String input) {
    return input
        .replace("&", "&amp;")
        .replace("\"", "&quot;")
        .replace("<", "&lt;")
        .replace(">", "&gt;");
  }

  private String getBaseUrl(HttpServletRequest request) {
    return ServletUriComponentsBuilder.fromRequestUri(request)
        .replacePath("")
        .build()
        .toUriString();
  }
}
