import 'dart:convert';
import 'dart:typed_data';
import 'package:intl/intl.dart';
import 'package:encrypt/encrypt.dart' as enc;
import 'package:crypto/crypto.dart';
import '../models/trip.dart';
import '../models/photo.dart';
import '../models/place.dart';
import '../core/preferences.dart';

class HtmlGenerator {
  static String getDayColor(int dayNumber) {
    const colors = [
      '#2563eb', // Blue
      '#10b981', // Emerald Green
      '#f97316', // Orange
      '#8b5cf6', // Violet
      '#ef4444', // Red
      '#14b8a6', // Teal
      '#6366f1', // Indigo
      '#ec4899', // Pink
      '#f59e0b', // Amber
    ];
    return colors[(dayNumber - 1) % colors.length];
  }

  static String _encryptData(String plainText, String password) {
    final keyBytes = sha256.convert(utf8.encode(password)).bytes;
    final key = enc.Key(Uint8List.fromList(keyBytes));
    
    final ivBytes = sha256.convert(utf8.encode('${password}_iv')).bytes.sublist(0, 16);
    final iv = enc.IV(Uint8List.fromList(ivBytes));
    
    final encrypter = enc.Encrypter(enc.AES(key, mode: enc.AESMode.cbc));
    final encrypted = encrypter.encrypt(plainText, iv: iv);
    return encrypted.base64;
  }

  static String generateHtml(Trip trip, List<Place> places, List<Photo> photos, {bool forExport = false, String? password}) {
    final startDateText = trip.startTimeUtc != null
        ? DateFormat('d. M. yyyy').format(DateTime.fromMillisecondsSinceEpoch(trip.startTimeUtc!))
        : '?';
    final finishDateText = trip.finishTimeUtc != null
        ? DateFormat('d. M. yyyy').format(DateTime.fromMillisecondsSinceEpoch(trip.finishTimeUtc!))
        : '?';

    // 1. Group days
    final Set<int> dayNumbers = places.map((p) => p.dayNumber).toSet();
    final sortedDayNumbers = dayNumbers.toList()..sort();

    final List<Map<String, dynamic>> daysJson = [];
    for (final dayNum in sortedDayNumbers) {
      final dayPlaces = places.where((p) => p.dayNumber == dayNum).toList();
      final dayTitle = dayPlaces
          .map((p) => p.name)
          .where((name) => name != null && name.isNotEmpty)
          .join(' → ');

      daysJson.add({
        'day': dayNum,
        'label': 'Den $dayNum',
        'color': getDayColor(dayNum),
        'title': dayTitle.isNotEmpty ? dayTitle : 'Program dne',
        'summary': 'Trasa dne a navštívená místa.',
      });
    }

    // 2. Format points (places)
    final List<Map<String, dynamic>> pointsJson = [];
    for (var i = 0; i < places.length; i++) {
      final place = places[i];
      
      // Get photos for this place
      final placePhotos = photos.where((p) {
        if (place.arrivalTimeUtc == null || place.departureTimeUtc == null) return false;
        final time = p.dateTakenUtc ?? 0;
        return time >= place.arrivalTimeUtc! && time <= place.departureTimeUtc!;
      }).toList();

      final List<Map<String, dynamic>> processedPhotos = placePhotos.map((photo) {
        if (forExport) {
          final name = photo.displayName.trim();
          final safeName = name.isEmpty
              ? '${photo.id}.jpg'
              : (name.contains('.') ? name : '$name.jpg');
          final relativePath = 'Fotky/$safeName';
          return {
            'web': relativePath,
            'thumb': relativePath,
          };
        } else {
          return {
            'web': photo.contentUri,
            'thumb': photo.contentUri,
          };
        }
      }).toList();

      pointsJson.add({
        'id': place.id,
        'day': place.dayNumber,
        'lat': place.latitude,
        'lon': place.longitude,
        'name': place.name ?? 'Zastávka',
        'type': 'zastávka',
        'description': place.description ?? '',
        'order': i,
        'processedPhotos': processedPhotos,
      });
    }

    final vacationData = {
      'title': trip.name,
      'dateText': '$startDateText – $finishDateText',
      'description': '',
      'colorScheme': 'blue', // Default scheme
      'days': daysJson,
      'points': pointsJson,
    };

    final vacationDataJson = jsonEncode(vacationData);

    final isCs = AppPreferences.language == 'cs';
    final lockTitle = isCs ? 'Stránka je uzamčena' : 'Page is locked';
    final lockDesc = isCs
        ? 'Pro zobrazení dovolené zadejte přístupové heslo.'
        : 'Enter the password to view the vacation.';
    final lockPlaceholder = isCs ? 'Zadejte heslo...' : 'Enter password...';
    final lockUnlock = isCs ? 'Odemknout' : 'Unlock';
    final lockIncorrect = isCs ? 'Nesprávné heslo!' : 'Incorrect password!';

    final isEncrypted = password != null && password.isNotEmpty;
    final cryptoJsScript = isEncrypted
        ? '<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>'
        : '';

    final loginOverlayCss = isEncrypted
        ? '''
    /* Login box style */
    #login-overlay {
      position: fixed;
      top: 0;
      left: 0;
      width: 100vw;
      height: 100vh;
      background-color: #12181f;
      display: flex;
      justify-content: center;
      align-items: center;
      z-index: 9999;
      font-family: 'Outfit', sans-serif;
    }
    .login-box {
      background-color: #1a222d;
      border: 1px solid #2d3b4e;
      padding: 40px;
      border-radius: 12px;
      width: 90%;
      max-width: 400px;
      text-align: center;
      box-shadow: 0 10px 30px rgba(0,0,0,0.3);
    }
    .login-box .icon {
      font-size: 40px;
      margin-bottom: 15px;
    }
    .login-box h2 {
      font-size: 20px;
      color: #f0f4f8;
      margin-bottom: 10px;
    }
    .login-box p {
      font-size: 13px;
      color: #8b9bb4;
      margin-bottom: 20px;
    }
    .login-box input {
      width: 100%;
      padding: 12px;
      border-radius: 6px;
      border: 1px solid #2d3b4e;
      background-color: #242f3e;
      color: #f0f4f8;
      font-size: 14px;
      outline: none;
      margin-bottom: 15px;
      text-align: center;
      box-sizing: border-box;
    }
    .login-box button {
      width: 100%;
      padding: 12px;
      background-color: #2563eb;
      color: white;
      border: none;
      border-radius: 6px;
      font-size: 14px;
      font-weight: 600;
      cursor: pointer;
    }
    .login-box button:hover {
      background-color: #3b82f6;
    }
        '''
        : '';

    final loginOverlayHtml = isEncrypted
        ? '''
  <!-- ZABEZPEČENÍ HESLEM (PŘIHLAŠOVACÍ OBRAZOVKA) -->
  <div id="login-overlay">
    <div class="login-box">
      <div class="icon">🔒</div>
      <h2>$lockTitle</h2>
      <p>$lockDesc</p>
      <form id="login-form">
        <input type="password" id="pass-input" placeholder="$lockPlaceholder" required autofocus>
        <button type="submit">$lockUnlock</button>
      </form>
      <p id="login-error" style="color: #ef4444; font-size: 13px; margin-top: 10px; display: none;">$lockIncorrect</p>
    </div>
  </div>
        '''
        : '';

    final appStyleAttr = isEncrypted ? 'style="display: none;"' : '';

    final String vacationDataJs;
    if (isEncrypted) {
      final encryptedData = _encryptData(vacationDataJson, password);
      vacationDataJs = '''
    const isEncrypted = true;
    const encryptedData = "$encryptedData";
    let vacationData = null;
      ''';
    } else {
      vacationDataJs = '''
    const isEncrypted = false;
    const encryptedData = "";
    let vacationData = $vacationDataJson;
      ''';
    }

    return '''<!DOCTYPE html>
<html lang="cs">
<head>
  <meta charset="utf-8">
  $cryptoJsScript
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>${trip.name}</title>
  <link rel="preconnect" href="https://fonts.googleapis.com">
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
  <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700&display=swap" rel="stylesheet">
  <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
  <style>
    body.scheme-light {
      --bg: #f3f4f6;
      --paper: #ffffff;
      --ink: #1f2937;
      --muted: #6b7280;
      --border: #e5e7eb;
      --panel-shadow: rgba(0, 0, 0, 0.08);
      --header-grad: linear-gradient(135deg, #1e3a8a, #3b82f6);
    }
    body.scheme-dark {
      --bg: #0f172a;
      --paper: #1e293b;
      --ink: #f8fafc;
      --muted: #94a3b8;
      --border: #334155;
      --panel-shadow: rgba(0, 0, 0, 0.3);
      --header-grad: linear-gradient(135deg, #0f172a, #1e293b);
    }
    body.scheme-blue {
      --bg: #0f172a;
      --paper: #1e3a8a;
      --ink: #f0f4f8;
      --muted: #93c5fd;
      --border: #1e40af;
      --panel-shadow: rgba(0, 0, 0, 0.25);
      --header-grad: linear-gradient(135deg, #1e3a8a, #2563eb);
    }
    body.scheme-green {
      --bg: #022c22;
      --paper: #064e3b;
      --ink: #f0fdf4;
      --muted: #6ee7b7;
      --border: #065f46;
      --panel-shadow: rgba(0, 0, 0, 0.25);
      --header-grad: linear-gradient(135deg, #022c22, #059669);
    }
    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
    }
    body {
      font-family: 'Outfit', system-ui, -apple-system, sans-serif;
      height: 100vh;
      margin: 0;
      color: var(--ink);
      background-color: var(--bg);
      overflow: hidden;
      transition: all 0.3s ease;
    }
    #app {
      display: grid;
      grid-template-columns: 380px 1fr;
      height: 100%;
    }
    aside {
      background-color: var(--paper);
      border-right: 1px solid var(--border);
      box-shadow: 0 0 24px var(--panel-shadow);
      display: flex;
      flex-direction: column;
      height: 100%;
      z-index: 1000;
      overflow: hidden;
    }
    header {
      padding: 24px 20px 16px;
      background: var(--header-grad);
      color: #ffffff;
    }
    header h1 {
      font-size: 22px;
      font-weight: 700;
      margin-bottom: 6px;
    }
    header p {
      font-size: 13px;
      opacity: 0.85;
    }
    header .intro-desc {
      font-size: 13px;
      margin-top: 10px;
      line-height: 1.45;
      opacity: 0.9;
    }
    .toolbar {
      padding: 12px;
      background-color: var(--paper);
      border-bottom: 1px solid var(--border);
      display: flex;
      gap: 8px;
      position: sticky;
      top: 0;
      z-index: 10;
    }
    .toolbar button {
      font-family: inherit;
      flex: 1;
      font-size: 11px;
      font-weight: 600;
      padding: 8px 6px;
      background-color: var(--bg);
      color: var(--ink);
      border: 1px solid var(--border);
      border-radius: 6px;
      cursor: pointer;
      transition: all 0.2s ease;
    }
    .toolbar button:hover {
      background-color: var(--border);
    }
    .days-list {
      flex: 1;
      overflow-y: auto;
      padding: 12px;
      display: flex;
      flex-direction: column;
      gap: 12px;
    }
    .day-card {
      background-color: var(--bg);
      border: 1px solid var(--border);
      border-left: 6px solid var(--c);
      border-radius: 8px;
      padding: 14px;
      display: flex;
      flex-direction: column;
      gap: 8px;
    }
    .day-head {
      display: flex;
      align-items: flex-start;
      gap: 10px;
    }
    .day-head input {
      margin-top: 3px;
      accent-color: var(--c);
      width: 16px;
      height: 16px;
      cursor: pointer;
    }
    .day-info {
      flex: 1;
      cursor: pointer;
    }
    .day-name {
      font-size: 14px;
      font-weight: 700;
    }
    .day-title {
      font-size: 12px;
      color: var(--muted);
      margin-top: 2px;
    }
    .day-summary {
      font-size: 12px;
      line-height: 1.45;
      color: var(--ink);
      padding-left: 26px;
      opacity: 0.95;
    }
    .day-photos {
      display: flex;
      gap: 6px;
      overflow-x: auto;
      padding-left: 26px;
      margin-top: 6px;
      scrollbar-width: thin;
    }
    .day-photos img {
      width: 60px;
      height: 60px;
      object-fit: cover;
      border-radius: 4px;
      cursor: pointer;
      border: 1px solid var(--border);
      transition: transform 0.2s ease;
    }
    .day-photos img:hover {
      transform: scale(1.05);
    }
    .footer-note {
      padding: 12px;
      font-size: 11px;
      color: var(--muted);
      text-align: center;
      border-top: 1px solid var(--border);
      background-color: var(--paper);
    }
    #map {
      height: 100%;
      width: 100%;
    }
    .pin {
      width: 30px;
      height: 30px;
      border-radius: 50% 50% 50% 0;
      transform: rotate(-45deg);
      border: 2px solid white;
      box-shadow: 0 3px 6px rgba(0,0,0,0.3);
      display: flex;
      align-items: center;
      justify-content: center;
    }
    .pin > span {
      transform: rotate(45deg);
      color: white;
      font-size: 11px;
      font-weight: 700;
    }
    .pin.stay {
      width: 34px;
      height: 34px;
      border-width: 3px;
    }
    .pin.stay::after {
      content: "";
      position: absolute;
      width: 7px;
      height: 7px;
      border-radius: 50%;
      background: white;
      right: -2px;
      top: -2px;
      box-shadow: 0 1px 3px rgba(0,0,0,0.35);
    }
    .leaflet-popup-content-wrapper {
      background-color: var(--paper) !important;
      color: var(--ink) !important;
      border-radius: 8px !important;
      border: 1px solid var(--border);
      font-family: inherit;
    }
    .leaflet-popup-tip {
      background-color: var(--paper) !important;
    }
    .popup-content h3 {
      font-size: 14px;
      font-weight: 700;
      margin-bottom: 4px;
    }
    .popup-content .meta {
      font-size: 11px;
      color: var(--muted);
      margin-bottom: 6px;
    }
    .popup-content p {
      font-size: 12px;
      line-height: 1.4;
      margin-bottom: 8px;
    }
    .popup-photos {
      display: flex;
      gap: 4px;
      overflow-x: auto;
      margin-bottom: 6px;
    }
    .popup-photos img {
      width: 50px;
      height: 50px;
      object-fit: cover;
      border-radius: 3px;
      cursor: pointer;
    }
    .popup-links a {
      font-size: 11px;
      color: #3b82f6;
      text-decoration: none;
      font-weight: 600;
    }
    .popup-links a:hover {
      text-decoration: underline;
    }
    #gallery-modal {
      display: none;
      position: fixed;
      z-index: 15000;
      padding-top: 60px;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      overflow: auto;
      background-color: rgba(0, 0, 0, 0.95);
    }
    .gallery-content {
      margin: auto;
      display: block;
      width: 80%;
      max-width: 1000px;
      max-height: 80vh;
      object-fit: contain;
      border-radius: 4px;
    }
    #gallery-caption {
      margin: auto;
      display: block;
      width: 80%;
      max-width: 1000px;
      text-align: center;
      color: #ccc;
      padding: 15px 0;
      font-size: 15px;
    }
    .close-gallery {
      position: absolute;
      top: 15px;
      right: 35px;
      color: #f1f1f1;
      font-size: 40px;
      font-weight: bold;
      cursor: pointer;
      transition: 0.3s;
    }
    .close-gallery:hover {
      color: #bbb;
    }
    .nav-gallery {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      background: rgba(255, 255, 255, 0.15);
      border: 1.5px solid rgba(255, 255, 255, 0.3);
      color: white;
      font-size: 32px;
      padding: 12px;
      cursor: pointer;
      border-radius: 50%;
      width: 58px;
      height: 58px;
      display: flex;
      align-items: center;
      justify-content: center;
      transition: all 0.2s ease;
      user-select: none;
      z-index: 16000;
    }
    .nav-gallery:hover {
      background: rgba(255, 255, 255, 0.35);
      transform: translateY(-50%) scale(1.08);
    }
    .nav-gallery.prev { left: 25px; }
    .nav-gallery.next { right: 25px; }
    
    .sidebar-toggle-btn {
      display: none;
    }
    .sidebar-close-btn {
      display: none;
      background: rgba(0, 0, 0, 0.4);
      border: 1px solid rgba(255, 255, 255, 0.3);
      color: white;
      border-radius: 50%;
      width: 32px;
      height: 32px;
      font-size: 16px;
      cursor: pointer;
      align-items: center;
      justify-content: center;
      transition: all 0.2s;
    }
    .sidebar-close-btn:hover {
      background: rgba(0, 0, 0, 0.6);
      transform: scale(1.05);
    }
    @media (max-width: 768px) {
      .nav-gallery {
        font-size: 20px;
        width: 46px;
        height: 46px;
        background: rgba(0, 0, 0, 0.6);
        border: 1px solid rgba(255, 255, 255, 0.2);
      }
      .nav-gallery.prev { left: 10px; }
      .nav-gallery.next { right: 10px; }
      #app {
        grid-template-columns: 1fr;
        height: 100%;
        position: relative;
      }
      aside {
        position: absolute;
        top: 0;
        left: 0;
        width: 310px;
        height: 100%;
        z-index: 1500;
        transform: translateX(-100%);
        transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        border-right: 1px solid var(--border);
      }
      aside.open {
        transform: translateX(0);
      }
      #map { height: 100%; width: 100%; }
      header { padding: 16px 16px 12px; }
      header h1 { font-size: 18px; }
      header .intro-desc { display: none; }
      .day-summary { display: block; }
      .day-photos { padding-left: 0; }
      
      .sidebar-toggle-btn {
        display: flex;
        position: absolute;
        top: 15px;
        left: 15px;
        z-index: 1100;
        background: var(--paper);
        color: var(--ink);
        border: 1px solid var(--border);
        border-radius: 8px;
        width: 40px;
        height: 40px;
        font-size: 20px;
        cursor: pointer;
        align-items: center;
        justify-content: center;
        box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
        transition: all 0.2s ease;
      }
      .sidebar-toggle-btn:hover {
        background: var(--border);
        transform: scale(1.05);
      }
      .sidebar-close-btn {
        display: flex;
      }
    }
    
    .web-color-options { display: flex; gap: 6px; }
    .web-color-option {
      width: 18px;
      height: 18px;
      border-radius: 50%;
      cursor: pointer;
      border: 1.5px solid transparent;
      transition: all 0.2s ease;
      box-shadow: 0 1px 3px rgba(0,0,0,0.3);
    }
    .web-color-option:hover { transform: scale(1.2); }
    .web-color-option.active {
      border-color: var(--ink) !important;
      transform: scale(1.1);
      box-shadow: 0 0 0 1.5px var(--border);
    }
    .web-color-option.light { background-color: #ffffff; border-color: #cbd5e1; }
    .web-color-option.dark { background-color: #12181f; border-color: #3b4252; }
    .web-color-option.blue { background-color: #1e3a8a; border-color: #3b82f6; }
    .web-color-option.green { background-color: #064e3b; border-color: #10b981; }

    .map-legend {
      position: absolute;
      bottom: 25px;
      right: 10px;
      z-index: 1000;
      background-color: var(--paper);
      border: 1px solid var(--border);
      border-radius: 8px;
      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
      display: flex;
      flex-direction: column;
      align-items: flex-end;
      max-width: 280px;
      transition: all 0.3s ease;
      font-family: inherit;
    }
    .legend-toggle-btn {
      background: var(--paper);
      color: var(--ink);
      border: 1px solid var(--border);
      padding: 8px 12px;
      border-radius: 8px;
      font-size: 13px;
      font-weight: 600;
      cursor: pointer;
      display: flex;
      align-items: center;
      gap: 6px;
      box-shadow: 0 2px 5px rgba(0,0,0,0.1);
      transition: background 0.2s;
    }
    .legend-toggle-btn:hover { background: var(--border); }
    .legend-content {
      display: none;
      width: 260px;
      padding: 12px;
      border-top: 1px solid var(--border);
      max-height: 250px;
      overflow-y: auto;
    }
    .map-legend.expanded .legend-content { display: block; }
    .legend-content h4 {
      font-size: 13px;
      font-weight: 700;
      margin-top: 0;
      margin-bottom: 8px;
      color: var(--ink);
    }
    .legend-item {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 12px;
      margin-bottom: 6px;
      color: var(--ink);
      text-align: left;
    }
    .legend-color-dot {
      width: 12px;
      height: 12px;
      border-radius: 50%;
      flex-shrink: 0;
      border: 1px solid rgba(0,0,0,0.1);
    }
    .legend-day-num { font-weight: 700; color: var(--ink); }

    /* Lang Switcher Styles */
    .lang-switch {
      position: absolute;
      top: 15px;
      right: 15px;
      z-index: 1000;
    }
    .lang-toggle-btn {
      background: var(--paper);
      color: var(--ink);
      border: 1px solid var(--border);
      padding: 8px 12px;
      border-radius: 8px;
      font-size: 13px;
      font-weight: 600;
      cursor: pointer;
      box-shadow: 0 2px 5px rgba(0,0,0,0.1);
      display: flex;
      align-items: center;
      gap: 6px;
      transition: all 0.2s;
    }
    .lang-toggle-btn:hover {
      background: var(--border);
      transform: scale(1.05);
    }
    $loginOverlayCss
  </style>
</head>
<body class="scheme-blue">
  $loginOverlayHtml
  <div id="app" $appStyleAttr>
    <button id="btn-sidebar-toggle" class="sidebar-toggle-btn" aria-label="Menu">☰</button>
    <aside>
      <header id="web-header">
        <div style="display: flex; justify-content: space-between; align-items: flex-start; gap: 10px;">
          <div style="flex: 1;">
            <h1 id="vac-title" style="margin: 0; font-size: 24px; line-height: 1.2;">Dovolená</h1>
            <p id="vac-date" style="margin-top: 4px; margin-bottom: 0;">Termín dovolené</p>
          </div>
          <div style="display: flex; flex-direction: column; align-items: flex-end; gap: 8px; flex-shrink: 0;">
            <button id="btn-sidebar-close" class="sidebar-close-btn" aria-label="Zavřít">&#x2715;</button>
            <div class="web-color-options" style="display: flex; gap: 6px; margin-top: 4px;">
              <div class="web-color-option light" data-scheme="light" title="Světlý"></div>
              <div class="web-color-option dark" data-scheme="dark" title="Tmavý"></div>
              <div class="web-color-option blue" data-scheme="blue" title="Modrý"></div>
              <div class="web-color-option green" data-scheme="green" title="Zelený"></div>
            </div>
          </div>
        </div>
        <p id="vac-desc" class="intro-desc" style="margin-top: 12px;">Úvodní slovo...</p>
      </header>
      
      <div class="toolbar">
        <button id="btn-show-all">Zobrazit vše</button>
        <button id="btn-show-stays">Jen ubytování</button>
        <button id="btn-toggle-routes">Skrýt spojnice</button>
      </div>

      <div class="days-list" id="days-wrap"></div>

      <div class="footer-note">
        Spojnice znázorňují pořadí, nikoli přesnou silniční trasu.
      </div>
    </aside>
    
    <div id="map"></div>

    <!-- PŘEPÍNAČ JAZYKŮ -->
    <div class="lang-switch">
      <button id="btn-lang-toggle" class="lang-toggle-btn">🇨🇿 CZ</button>
    </div>
    
    <div id="map-legend" class="map-legend">
      <button id="btn-legend-toggle" class="legend-toggle-btn">🗺️ Legenda</button>
      <div class="legend-content">
        <h4 id="legend-title-text">Itinerář po dnech</h4>
        <div id="legend-list"></div>
      </div>
    </div>
  </div>

  <div id="gallery-modal">
    <span class="close-gallery" id="close-gallery">&times;</span>
    <button class="nav-gallery prev" id="btn-gallery-prev" aria-label="Předchozí fotka">&#10094;</button>
    <img class="gallery-content" id="gallery-img">
    <button class="nav-gallery next" id="btn-gallery-next" aria-label="Následující fotka">&#10095;</button>
    <div id="gallery-caption">Popis fotky</div>
  </div>

  <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
  <script>
    $vacationDataJs

    let map;
    let markerMode = 'all';
    let routesVisible = true;
    const layers = {};
    const markerStore = {};
    const bounds = [];
    let galleryPhotos = [];
    let currentPhotoIndex = -1;

    const translations = {
      en: {
        vacTitle: "Vacation",
        vacDate: "Vacation Dates",
        btnShowAll: "Show All",
        btnShowStays: "Stays Only",
        btnHideRoutes: "Hide Routes",
        btnShowRoutes: "Show Routes",
        footerNote: "Lines show chronological order, not the exact road route.",
        legendTitle: "Daily Itinerary",
        legendToggle: "🗺️ Legend",
        legendClose: "❌ Close",
        dayLabel: "Day",
        defaultProgram: "Daily Program",
        noNotes: "No notes for this day.",
        accommodation: "Accommodation",
        stop: "Stop",
        noDescription: "No description available.",
        defaultRouteDesc: "Route and places visited.",
        btnLangText: "🇨🇿 CZ"
      },
      cs: {
        vacTitle: "Dovolená",
        vacDate: "Termín dovolené",
        btnShowAll: "Zobrazit vše",
        btnShowStays: "Jen ubytování",
        btnHideRoutes: "Skrýt spojnice",
        btnShowRoutes: "Zobrazit spojnice",
        footerNote: "Spojnice znázorňují pořadí, nikoli přesnou silniční trasu.",
        legendTitle: "Itinerář po dnech",
        legendToggle: "🗺️ Legenda",
        legendClose: "❌ Zavřít",
        dayLabel: "Den",
        defaultProgram: "Program dne",
        noNotes: "Žádné zápisky k tomuto dni.",
        accommodation: "Ubytování",
        stop: "Zastávka",
        noDescription: "Bez bližšího popisu.",
        defaultRouteDesc: "Trasa a navštívená místa.",
        btnLangText: "🇬🇧 EN"
      }
    };

    let currentLang = '${AppPreferences.language}';

    document.addEventListener('DOMContentLoaded', () => {
      const langToggle = document.getElementById('btn-lang-toggle');
      if (langToggle) {
        langToggle.textContent = currentLang === 'en' ? '🇨🇿 CZ' : '🇬🇧 EN';
        langToggle.addEventListener('click', () => {
          currentLang = currentLang === 'en' ? 'cs' : 'en';
          langToggle.textContent = currentLang === 'en' ? '🇨🇿 CZ' : '🇬🇧 EN';
          if (vacationData) {
            initMapAndUI(vacationData);
          }
        });
      }

      // Legend toggle event listener registered ONCE
      const legendToggle = document.getElementById('btn-legend-toggle');
      const mapLegend = document.getElementById('map-legend');
      if (legendToggle && mapLegend) {
        legendToggle.addEventListener('click', (e) => {
          e.stopPropagation();
          mapLegend.classList.toggle('expanded');
          const isExpanded = mapLegend.classList.contains('expanded');
          const t = translations[currentLang];
          legendToggle.textContent = isExpanded ? t.legendClose : t.legendToggle;
        });
      }

      const sidebarToggle = document.getElementById('btn-sidebar-toggle');
      const sidebarClose = document.getElementById('btn-sidebar-close');
      const sidebar = document.querySelector('aside');

      if (sidebarToggle && sidebar && sidebarClose) {
        sidebarToggle.addEventListener('click', (e) => {
          e.stopPropagation();
          sidebar.classList.add('open');
        });
        sidebarClose.addEventListener('click', (e) => {
          e.stopPropagation();
          sidebar.classList.remove('open');
        });
      }

      if (isEncrypted) {
        document.getElementById('login-overlay').style.display = 'flex';
        document.getElementById('login-form').addEventListener('submit', handleLogin);
      } else {
        initMapAndUI(vacationData);
      }

      const modal = document.getElementById('gallery-modal');
      document.getElementById('close-gallery').addEventListener('click', () => {
        modal.style.display = 'none';
      });

      const btnPrev = document.getElementById('btn-gallery-prev');
      const btnNext = document.getElementById('btn-gallery-next');

      if (btnPrev) btnPrev.addEventListener('click', (e) => { e.stopPropagation(); navigateGallery(-1); });
      if (btnNext) btnNext.addEventListener('click', (e) => { e.stopPropagation(); navigateGallery(1); });

      modal.addEventListener('click', (e) => {
        if (e.target === modal || e.target.id === 'gallery-caption') {
          modal.style.display = 'none';
        }
      });

      window.addEventListener('keydown', (e) => {
        const isGalleryVisible = modal.style.display === 'block';
        if (e.key === 'Escape') {
          if (isGalleryVisible) modal.style.display = 'none';
          else if (map) map.closePopup();
        } else if (isGalleryVisible) {
          if (e.key === 'ArrowRight' || e.key === 'Space') navigateGallery(1);
          else if (e.key === 'ArrowLeft') navigateGallery(-1);
        }
      });

      let touchstartX = 0;
      let touchendX = 0;
      modal.addEventListener('touchstart', e => { touchstartX = e.changedTouches[0].screenX; }, { passive: true });
      modal.addEventListener('touchend', e => { touchendX = e.changedTouches[0].screenX; handleSwipeGesture(); }, { passive: true });

      function handleSwipeGesture() {
        const threshold = 50;
        if (touchendX < touchstartX - threshold) navigateGallery(1);
        if (touchendX > touchstartX + threshold) navigateGallery(-1);
      }
    });

    function handleLogin(e) {
      e.preventDefault();
      document.getElementById('login-error').style.display = 'none';
      const pass = document.getElementById('pass-input').value;
      try {
        const key = CryptoJS.SHA256(pass);
        const iv = CryptoJS.enc.Hex.parse(CryptoJS.SHA256(pass + "_iv").toString().substring(0, 32));
        const decryptedBytes = CryptoJS.AES.decrypt(encryptedData, key, { iv: iv });
        const decryptedText = decryptedBytes.toString(CryptoJS.enc.Utf8);
        if (!decryptedText) {
          document.getElementById('login-error').style.display = 'block';
          return;
        }
        const data = JSON.parse(decryptedText);
        document.getElementById('login-overlay').style.display = 'none';
        document.getElementById('app').style.display = 'grid';
        vacationData = data;
        initMapAndUI(data);
      } catch (err) {
        console.error("Decryption error:", err);
        document.getElementById('login-error').style.display = 'block';
      }
    }

    function initMapAndUI(data) {
      const t = translations[currentLang];
      const scheme = data.colorScheme || 'blue';
      document.body.className = `scheme-\${scheme}`;
      
      document.getElementById('vac-title').textContent = data.title || t.vacTitle;
      document.getElementById('vac-date').textContent = data.dateText || t.vacDate;
      
      const descEl = document.getElementById('vac-desc');
      if (data.description) {
        descEl.textContent = data.description;
        descEl.style.display = 'block';
      } else {
        descEl.style.display = 'none';
      }

      // Re-populate toolbar button texts
      document.getElementById('btn-show-all').textContent = t.btnShowAll;
      document.getElementById('btn-show-stays').textContent = t.btnShowStays;
      document.getElementById('btn-toggle-routes').textContent = routesVisible ? t.btnHideRoutes : t.btnShowRoutes;
      document.querySelector('.footer-note').textContent = t.footerNote;

      if (map) {
        map.remove();
      }
      Object.keys(layers).forEach(key => delete layers[key]);
      Object.keys(markerStore).forEach(key => delete markerStore[key]);
      bounds.length = 0;

      map = L.map('map', { zoomControl: false }).setView([48.0, 14.0], 5);
      L.control.zoom({ position: 'bottomright' }).addTo(map);
      
      map.on('click', () => {
        const sidebar = document.querySelector('aside');
        if (sidebar && window.innerWidth <= 768) {
          sidebar.classList.remove('open');
        }
      });

      const isDark = scheme === 'dark';
      const tileUrl = isDark 
        ? 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png'
        : 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png';
        
      L.tileLayer(tileUrl, {
        maxZoom: 19,
        attribution: '&copy; OpenStreetMap'
      }).addTo(map);

      L.control.scale({ imperial: false }).addTo(map);

      data.days.forEach(d => {
        markerStore[d.day] = [];
        layers[d.day] = {
          markers: L.layerGroup().addTo(map),
          route: L.layerGroup().addTo(map)
        };
      });

      data.points.forEach(p => {
        const isStay = p.type === 'ubytování';
        const dayObj = data.days.find(d => d.day === p.day);
        const dayColor = dayObj ? dayObj.color : '#2563eb';

        const markerHtml = `<div class="pin \${isStay ? 'stay' : ''}" style="background:\${dayColor}"><span>\${p.day}</span></div>`;
        const icon = L.divIcon({
          html: markerHtml,
          className: '',
          iconSize: [34, 34],
          iconAnchor: [17, 31],
          popupAnchor: [0, -27]
        });

        const marker = L.marker([p.lat, p.lon], { icon });

        let photosHtml = '';
        if (p.processedPhotos && p.processedPhotos.length > 0) {
          photosHtml = '<div class="popup-photos">';
          p.processedPhotos.forEach(photo => {
            photosHtml += `<img src="\${photo.thumb}" onclick="openGallery('\${photo.web}', \${p.day})" alt="foto">`;
          });
          photosHtml += '</div>';
        }

        const gMapsQuery = encodeURIComponent(`\${p.lat},\${p.lon}`);
        const typeLabel = isStay ? t.accommodation : (p.type === 'zastávka' ? t.stop : esc(p.type));
        const popupHtml = `
          <div class="popup-content">
            <h3>\${esc(p.name)}</h3>
            <div class="meta">📅 \${t.dayLabel} \${p.day} · \${typeLabel}</div>
            <p>\${esc(p.description || t.noDescription)}</p>
            \${photosHtml}
            <div class="popup-links">
              <a href="https://www.google.com/maps/search/?api=1&query=\${gMapsQuery}" target="_blank">Google Maps</a>
            </div>
          </div>
        `;

        marker.bindPopup(popupHtml);
        marker._isStay = isStay;
        marker._day = p.day;
        markerStore[p.day].push(marker);
        marker.addTo(layers[p.day].markers);
        bounds.push([p.lat, p.lon]);
      });

      data.days.forEach(d => {
        const dayPts = data.points.filter(p => p.day === d.day).sort((a, b) => a.order - b.order);
        if (dayPts.length > 1) {
          const coords = dayPts.map(p => [p.lat, p.lon]);
          L.polyline(coords, {
            color: d.color,
            weight: 4,
            opacity: 0.75,
            dashArray: '8, 8'
          }).addTo(layers[d.day].route);
        }
      });

      const daysWrap = document.getElementById('days-wrap');
      daysWrap.innerHTML = '';

      data.days.forEach(d => {
        const card = document.createElement('div');
        card.className = 'day-card';
        card.style.setProperty('--c', d.color);

        const dayPts = data.points.filter(p => p.day === d.day);
        let photosHtml = '';
        dayPts.forEach(pt => {
          if (pt.processedPhotos) {
            pt.processedPhotos.forEach(photo => {
              photosHtml += `<img src="\${photo.thumb}" onclick="openGallery('\${photo.web}', \${d.day})" alt="foto">`;
            });
          }
        });

        const dayLabelStr = `\${t.dayLabel} \${d.day}`;

        card.innerHTML = `
          <div class="day-head">
            <input type="checkbox" id="chk-day-\${d.day}" checked>
            <div class="day-info" onclick="focusDay(\${d.day})">
              <div class="day-name" style="color: \${d.color};">\${esc(dayLabelStr)}</div>
              <div class="day-title">\${esc(d.title || t.defaultProgram)}</div>
            </div>
          </div>
          <div class="day-summary">\${esc(d.summary || t.noNotes)}</div>
          \${photosHtml ? `<div class="day-photos">\${photosHtml}</div>` : ''}
        `;
        daysWrap.appendChild(card);
        card.querySelector('input').addEventListener('change', () => refreshDayLayer(d.day));
      });

      if (bounds.length > 0) {
        map.fitBounds(bounds, { padding: [30, 30] });
      }

      setupToolbar(data);
      setTimeout(() => { if (map) map.invalidateSize(); }, 100);

      const legendList = document.getElementById('legend-list');
      if (legendList) {
        legendList.innerHTML = '';
        data.days.forEach(d => {
          const item = document.createElement('div');
          item.className = 'legend-item';
          item.innerHTML = `
            <div class="legend-color-dot" style="background-color: \${d.color};"></div>
            <div>
              <span class="legend-day-num">\${t.dayLabel} \${d.day}:</span> 
              <span>\${esc(d.title || t.defaultProgram)}</span>
            </div>
          `;
          legendList.appendChild(item);
        });
      }

      const legendToggle = document.getElementById('btn-legend-toggle');
      const mapLegend = document.getElementById('map-legend');
      if (legendToggle && mapLegend) {
        document.getElementById('legend-title-text').textContent = t.legendTitle;
        const isExpanded = mapLegend.classList.contains('expanded');
        legendToggle.textContent = isExpanded ? t.legendClose : t.legendToggle;
      }

      const webColorOptions = document.querySelectorAll('.web-color-option');
      webColorOptions.forEach(opt => {
        if (opt.dataset.scheme === scheme) opt.classList.add('active');
        opt.addEventListener('click', () => {
          webColorOptions.forEach(o => o.classList.remove('active'));
          opt.classList.add('active');
          const newScheme = opt.dataset.scheme;
          document.body.className = `scheme-\${newScheme}`;
          if (map) {
            const isD = newScheme === 'dark';
            const newTileUrl = isD 
              ? 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png'
              : 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png';
            map.eachLayer(layer => {
              if (layer instanceof L.TileLayer) layer.setUrl(newTileUrl);
            });
          }
        });
      });
    }

    function refreshDayLayer(dayNum) {
      const isChecked = document.getElementById(`chk-day-\${dayNum}`).checked;
      if (map.hasLayer(layers[dayNum].markers)) map.removeLayer(layers[dayNum].markers);
      if (map.hasLayer(layers[dayNum].route)) map.removeLayer(layers[dayNum].route);

      const filteredMarkers = markerMode === 'stays'
        ? markerStore[dayNum].filter(m => m._isStay)
        : markerStore[dayNum];

      layers[dayNum].markers = L.layerGroup(filteredMarkers);
      if (isChecked) {
        layers[dayNum].markers.addTo(map);
        if (routesVisible && markerMode === 'all') {
          layers[dayNum].route.addTo(map);
        }
      }
    }

    function refreshAllDays() {
      Object.keys(layers).forEach(dayNum => refreshDayLayer(dayNum));
    }

    function focusDay(dayNum) {
      const dayMarkers = markerStore[dayNum];
      if (dayMarkers && dayMarkers.length > 0) {
        const dayBounds = dayMarkers.map(m => m.getLatLng());
        map.fitBounds(dayBounds, { padding: [50, 50], maxZoom: 14 });
        dayMarkers[0].openPopup();
      }
      const sidebar = document.querySelector('aside');
      if (sidebar && window.innerWidth <= 768) {
        sidebar.classList.remove('open');
      }
    }

    function setupToolbar(data) {
      // Setup reset handled globally in DOMContentLoaded
    }

    function openGallery(imgSrc, dayNum) {
      galleryPhotos = [];
      const dayPts = vacationData.points.filter(pt => pt.day === dayNum);
      dayPts.forEach(pt => {
        if (pt.processedPhotos) {
          pt.processedPhotos.forEach(photo => {
            if (!galleryPhotos.some(gp => gp.web === photo.web)) {
              const captionHtml = `<strong>\${esc(pt.name)}</strong>\${pt.description ? `<br>\${esc(pt.description)}` : ''}`;
              galleryPhotos.push({
                web: photo.web,
                caption: captionHtml
              });
            }
          });
        }
      });

      const idx = galleryPhotos.findIndex(p => p.web === imgSrc);
      if (idx !== -1) {
        showPhoto(idx);
      } else {
        const modal = document.getElementById('gallery-modal');
        const modalImg = document.getElementById('gallery-img');
        const captionEl = document.getElementById('gallery-caption');
        modal.style.display = 'block';
        modalImg.src = imgSrc;
        captionEl.innerHTML = '';
        currentPhotoIndex = -1;
      }
    }

    function showPhoto(index) {
      if (index < 0 || index >= galleryPhotos.length) return;
      currentPhotoIndex = index;
      const photo = galleryPhotos[index];
      const modal = document.getElementById('gallery-modal');
      const modalImg = document.getElementById('gallery-img');
      const captionEl = document.getElementById('gallery-caption');
      modal.style.display = 'block';
      modalImg.src = photo.web;
      captionEl.innerHTML = photo.caption || '';
    }

    function navigateGallery(direction) {
      if (galleryPhotos.length === 0) return;
      let newIdx = currentPhotoIndex + direction;
      if (newIdx >= galleryPhotos.length) newIdx = 0;
      if (newIdx < 0) newIdx = galleryPhotos.length - 1;
      showPhoto(newIdx);
    }

    function esc(str) {
      if (!str) return '';
      return String(str)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');
    }
  </script>
</body>
</html>''';
  }
}
