import 'package:mangayomi/bridge_lib.dart';
import 'dart:convert';

class Madara extends MProvider {
  Madara({required this.source});

  MSource source;

  final Client client = Client();

  MPages mangaFromElements(List<MElement> elements) {
    List<MManga> mangaList = [];

    for (final el in elements) {
      final postTitle = el.selectFirst(
        "div.post-title a:not(:has(span.manga-title-badges))",
      );
      final image = extractImageUrl(el.selectFirst("img"));

      MManga manga = MManga();
      manga.name = postTitle.text;
      manga.imageUrl = substringBefore(image, " ");
      manga.link = postTitle.getHref;
      mangaList.add(manga);
    }

    return MPages(mangaList, true);
  }

  @override
  Future<MPages> getPopular(int page) async {
    final res = (await client.get(
      Uri.parse(
        "${getBaseUrl()}/${getMangaSubString()}/page/$page/?m_orderby=views",
      ),
    )).body;
    final document = parseHtml(res);
    return mangaFromElements(document.select("div.page-item-detail"));
  }

  @override
  Future<MPages> getLatestUpdates(int page) async {
    final res = (await client.get(
      Uri.parse(
        "${getBaseUrl()}/${getMangaSubString()}/page/$page/?m_orderby=latest",
      ),
    )).body;
    final document = parseHtml(res);
    return mangaFromElements(document.select("div.page-item-detail"));
  }

  @override
  Future<MPages> search(String query, int page, FilterList filterList) async {
    final filters = filterList.filters;

    String url = "${getBaseUrl()}/?s=$query&post_type=wp-manga";

    for (var filter in filters) {
      if (filter.type == "AuthorFilter") {
        if (filter.state.isNotEmpty) {
          url += "${ll(url)}author=${Uri.encodeComponent(filter.state)}";
        }
      } else if (filter.type == "ArtistFilter") {
        if (filter.state.isNotEmpty) {
          url += "${ll(url)}artist=${Uri.encodeComponent(filter.state)}";
        }
      } else if (filter.type == "YearFilter") {
        if (filter.state.isNotEmpty) {
          url += "${ll(url)}release=${Uri.encodeComponent(filter.state)}";
        }
      } else if (filter.type == "StatusFilter") {
        List<String> status = filter.state
            .where((item) => item.state)
            .map((item) => item.value.toString())
            .toList();
        if (status.isNotEmpty) {
          url += "${ll(url)}status[]=${status.join('&status[]=')}";
        }
      } else if (filter.type == "OrderByFilter") {
        if (filter.state != 0) {
          final order = filter.values[filter.state].value;
          url += "${ll(url)}m_orderby=$order";
        }
      } else if (filter.type == "AdultContentFilter") {
        final ctn = filter.values[filter.state].value;
        if (ctn.isNotEmpty) {
          url += "${ll(url)}adult=$ctn";
        }
      } else if (filter.type == "GenreListFilter") {
        final genres = (filter.state as List).where((e) => e.state).toList();
        if (genres.isNotEmpty) {
          for (var genre in genres) {
            url += "${ll(url)}genre[]=${genre.value},";
          }
        }
      }
    }

    final res = (await client.get(Uri.parse(url))).body;
    final document = parseHtml(res);
    return mangaFromElements(document.select("div.c-tabs-item__content"));
  }

  List<MChapter> getChapters(MDocument chapDoc) {
    List<MChapter> chapters = [];
    for (MElement element in chapDoc.select("li.wp-manga-chapter") ?? []) {
      var ch = element.selectFirst("a");
      if (ch != null) {
        var url = ch.attr("href");
        if (url != null && url.isNotEmpty) {
          url = substringBefore(url, "?style=paged");
          if (url.endsWith("?style=paged")) {
            url = url + "?style=paged";
          }
          var chapter = MChapter();
          chapter.url = url;
          chapter.name = ch.text;
          if (source.dateFormat.isNotEmpty) {
            var chd = element.selectFirst("span.chapter-release-date");
            if (chd != null && chd.text.isNotEmpty) {
              var dates = parseDates(
                [chd.text],
                source.dateFormat,
                source.dateFormatLocale,
              );
              chapter.dateUpload = dates[0];
            } else {
              chapter.dateUpload = DateTime.now().millisecondsSinceEpoch
                  .toString();
            }
          }
          chapters.add(chapter);
        }
      }
    }
    return chapters;
  }

  @override
  Future<MManga> getDetail(String url) async {
    final statusList = [
      {
        // Ongoing
        "OnGoing": 0,
        "Продолжается": 0,
        "Updating": 0,
        "Em Lançamento": 0,
        "Em lançamento": 0,
        "Em andamento": 0,
        "Em Andamento": 0,
        "En cours": 0,
        "En Cours": 0,
        "En cours de publication": 0,
        "Ativo": 0,
        "Lançando": 0,
        "Đang Tiến Hành": 0,
        "Devam Ediyor": 0,
        "Devam ediyor": 0,
        "Devam Ediyo": 0,
        "Devam Eden": 0,
        "In Corso": 0,
        "In Arrivo": 0,
        "مستمرة": 0,
        "مستمر": 0,
        "En Curso": 0,
        "En curso": 0,
        "Curso": 0,
        "Emision": 0,
        "En marcha": 0,
        "Publicandose": 0,
        "Publicándose": 0,
        "En emision": 0,
        "连载中": 0,
        "Đang làm": 0,
        "Em postagem": 0,
        "Em progresso": 0,
        "Em curso": 0,
        "Atualizações Semanais": 0,

        // Completed
        "Completed": 1,
        "Completo": 1,
        "Completado": 1,
        "Concluído": 1,
        "Concluido": 1,
        "Finalizado": 1,
        "Achevé": 1,
        "Terminé": 1,
        "Complété": 1,
        "Hoàn Thành": 1,
        "Tamamlandı": 1,
        "Tamamlanan": 1,
        "Đã hoàn thành": 1,
        "Завершено": 1,
        "مكتملة": 1,
        "مكتمل": 1,
        "已完结": 1,

        // On Hold
        "On Hold": 2,
        "Pausado": 2,
        "En espera": 2,
        "Durduruldu": 2,
        "Beklemede": 2,
        "Đang chờ": 2,
        "متوقف": 2,
        "En Pause": 2,
        "Заморожено": 2,
        "En attente": 2,

        // Canceled
        "Canceled": 3,
        "Cancelado": 3,
        "İptal Edildi": 3,
        "Güncel": 3,
        "Đã hủy": 3,
        "ملغي": 3,
        "Abandonné": 3,
        "Заброшено": 3,
        "Annulé": 3,

        // Publishing Finished 4
      },
    ];
    MManga manga = MManga();
    String res = "";
    res = (await client.get(Uri.parse(url))).body;
    final document = parseHtml(res);
    manga.author = document.selectFirst("div.author-content > a")?.text ?? "";

    final descriptionElement = document.select(
      "div.description-summary div.summary__content, div.summary_content div.post-content_item > h5 + div, div.summary_content div.manga-excerpt, .manga-summary",
    );
    if (descriptionElement.isNotEmpty) {
      final paragraphs = descriptionElement
          .expand((e) => e.select("p"))
          .toList();

      if (paragraphs.isNotEmpty &&
          paragraphs.any((p) => p.text.trim().isNotEmpty)) {
        manga.description = paragraphs
            .map((p) => p.text.replaceAll("<br>", "\n").trim())
            .join("\n\n");
      } else {
        manga.description = descriptionElement
            .map((e) => e.text.trim())
            .join("\n\n");
      }
    }

    final imageElement = document.selectFirst("div.summary_image img");
    manga.imageUrl = extractImageUrl(imageElement);

    final id =
        document
            .selectFirst("div[id^=manga-chapters-holder]")
            ?.attr("data-id") ??
        "";
    String mangaId = "";
    if (id.isNotEmpty) {
      mangaId = id;
    }
    final status =
        document
            .selectFirst(
              ".summary-content > .tags-content, div.summary-content, div.summary-heading:contains(Status) + div",
            )
            ?.text ??
        "";

    manga.status = parseStatus(status, statusList);
    manga.genre =
        document.select("div.genres-content a")?.map((e) => e.text).toList() ??
        [];

    final baseUrl = "${getBaseUrl()}/";
    final headers = {"Referer": baseUrl, "X-Requested-With": "XMLHttpRequest"};

    final oldXhrChaptersRequest = await client.post(
      Uri.parse("${baseUrl}wp-admin/admin-ajax.php"),
      headers: headers,
      body: {"action": "manga_get_chapters", "manga": mangaId},
    );
    if (oldXhrChaptersRequest.statusCode == 400) {
      res = (await client.post(
        Uri.parse("${url}ajax/chapters"),
        headers: headers,
      )).body;
    } else {
      res = oldXhrChaptersRequest.body;
    }

    MDocument chapDoc = parseHtml(res);
    manga.chapters = getChapters(chapDoc);
    if (manga.chapters.isEmpty) {
      res = (await client.post(
        Uri.parse("${url}ajax/chapters"),
        headers: headers,
      )).body;
      chapDoc = parseHtml(res);
      manga.chapters = getChapters(chapDoc);
    }

    return manga;
  }

  @override
  Future<List<String>> getPageList(String url) async {
    final res = (await client.get(Uri.parse(url)));
    final document = parseHtml(res.body);

    var images = getImagesFromPage(document);
    if (images.length == 1) {
      images = buildPageUrls(images, document);
    }

    return images.isNotEmpty ? images : parseProtectorImage(document);
  }

  List<String> getImagesFromPage(MDocument doc) {
    final elements = doc.select(
      "div.page-break img, li.blocks-gallery-item img, .reading-content .text-left:not(:has(.blocks-gallery-item)) img",
    );
    return elements.map((e) => extractImageUrl(e)?.trim()).toList();
  }

  List<String> parseProtectorImage(Document doc) {
    final protectorData = doc.selectFirst(chapterProtectorSelector)?.innerHtml;
    if (protectorData == null) return [];

    final password = getPasswordFromProtector(protectorData);
    final chapterDataStr = getChapterDataStr(protectorData);

    if (chapterDataStr == null || password == null) return [];

    final decryptedText = decryptChapterData(chapterDataStr, password);
    if (decryptedText == null) return [];

    return List<String>.from(jsonDecode(jsonDecode(decryptedText)));
  }

  // Extract password
  String? getPasswordFromProtector(String protectorData) {
    final regex = RegExp(r"wpmangaprotectornonce='(.*?)';");
    final match = regex.firstMatch(protectorData);
    return match != null ? match.group(1) : null;
  }

  // Extract encrypted chapter string
  String? getChapterDataStr(String protectorData) {
    final regex = RegExp(r"chapter_data='(.*?)';");
    final match = regex.firstMatch(protectorData);
    return match != null ? match.group(1)!.replaceAll(r"\/", "/") : null;
  }

  // Decrypt AES encrypted string
  String? decryptChapterData(String chapterDataStr, String password) {
    try {
      final Map<String, dynamic> chapterData = jsonDecode(chapterDataStr);
      final salt = _hexToBytes(chapterData["s"]);
      final ct = base64Decode(chapterData["ct"]);

      // Prefix with 'Salted__' and salt
      final completeCipher = Uint8List.fromList([
        ...utf8.encode('Salted__'),
        ...salt,
        ...ct,
      ]);

      // Encode completeCipher again to base64
      final base64Cipher = base64Encode(completeCipher);

      // Decrypt
      final decrypted = decryptAESCryptoJS(base64Cipher, password);
      return decrypted;
    } catch (e) {
      print("Error decrypting chapter data: $e");
      return null;
    }
  }

  List<String> buildPageUrls(List<String> imgs, MDocument document) {
    List<String> pageUrls = [];
    final pagesNumber = document
        .selectFirst("#single-pager")
        .select("option")
        .length;

    final imgUrl = imgs.first;
    for (var i = 0; i < pagesNumber; i++) {
      final val = i + 1;
      if (i.toString().length == 1) {
        pageUrls.add(imgUrl.replaceAll("01", '0$val'));
      } else {
        pageUrls.add(imgUrl.replaceAll("01", val.toString()));
      }
    }

    return pageUrls;
  }

  // Convert hex string to bytes
  List<int> _hexToBytes(String hex) {
    final List<int> result = [];
    for (var i = 0; i < hex.length; i += 2) {
      result.add(int.parse(hex.substring(i, i + 2), radix: 16));
    }
    return result;
  }

  @override
  List<dynamic> getFilterList() {
    return [
      TextFilter("AuthorFilter", "Author"),
      TextFilter("ArtistFilter", "Artist"),
      TextFilter("YearFilter", "Year of Released"),
      GroupFilter("StatusFilter", "Status", [
        CheckBoxFilter("Completed", "end"),
        CheckBoxFilter("Ongoing", "on-going"),
        CheckBoxFilter("Canceled", "canceled"),
        CheckBoxFilter("On Hold", "on-hold"),
      ]),
      SelectFilter("OrderByFilter", "Order By", 0, [
        SelectFilterOption("Relevance", ""),
        SelectFilterOption("Latest", "latest"),
        SelectFilterOption("A-Z", "alphabet"),
        SelectFilterOption("Rating", "rating"),
        SelectFilterOption("Trending", "trending"),
        SelectFilterOption("Most Views", "views"),
        SelectFilterOption("New", "new-manga"),
      ]),
      SelectFilter("AdultContentFilter", "Adult Content", 0, [
        SelectFilterOption("All", ""),
        SelectFilterOption("None", "0"),
        SelectFilterOption("Only", "1"),
      ]),
    ];
  }

  @override
  List<dynamic> getSourcePreferences() {
    return [
      EditTextPreference(
        key: "domain_url",
        title: getTitleByLang(source.lang),
        summary: "",
        value: source.baseUrl,
        dialogTitle: "URL",
        dialogMessage: "",
      ),
    ];
  }

  String getBaseUrl() {
    final baseUrl = getPreferenceValue(source.id, "domain_url")?.trim();

    if (baseUrl == null || baseUrl.isEmpty) {
      return source.baseUrl;
    }

    return baseUrl.endsWith("/")
        ? baseUrl.substring(0, baseUrl.length - 1)
        : baseUrl;
  }

  String getTitleByLang(String? lang) {
    const titles = {
      'ar': 'تحرير الرابط',
      'en': 'Edit URL',
      'fr': 'Modifier l’URL',
      'es': 'Editar URL',
      'de': 'URL bearbeiten',
      'tr': 'URL’yi düzenle',
      'ru': 'Редактировать URL',
      'id': 'Edit URL',
      'pt': 'Editar URL',
      'it': 'Modifica URL',
      'ja': 'URLを編集',
      'zh': '编辑网址',
      'ko': 'URL 편집',
      'fa': 'ویرایش نشانی',
    };

    return titles[lang?.toLowerCase()] ?? titles['en']!;
  }

  String ll(String url) {
    if (url.contains("?")) {
      return "&";
    }
    return "?";
  }

  String? extractImageUrl(Element? imageElement) {
    if (imageElement == null) return "";

    return imageElement.attr("data-src") ??
        imageElement.attr("data-lazy-src") ??
        imageElement.attr("srcset")?.split(" ")?.first ??
        imageElement.getSrc ??
        "";
  }

  String getMangaSubString() {
    const sourceTypeMap = {
      "Olaoe": "works",
      "Mangax Core": "works",
      "Azora": "series",
    };

    return sourceTypeMap[source.name] ?? "manga";
  }
}

Madara main(MSource source) {
  return Madara(source: source);
}
