# -*- coding: utf-8 -*-
"""
Download Batas Wilayah - QGIS Processing Algorithm
Downloads administrative boundaries from BIG REST services.
"""

import os
import re
import json
import time
import tempfile
import requests

from qgis.core import (
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingParameterEnum,
    QgsProcessingParameterString,
    QgsProcessingParameterFolderDestination,
    QgsProcessingParameterFileDestination,
    QgsProcessingContext,
    QgsVectorLayer,
    QgsVectorFileWriter,
    QgsCoordinateReferenceSystem,
    QgsProject
)
from .license_guard import SupabaseGuard


class DownloadBatasWilayah(QgsProcessingAlgorithm):
    """Download batas wilayah administrasi dari layanan REST BIG."""

    TINGKAT = 'TINGKAT'
    FILTER_PROVINSI = 'FILTER_PROVINSI'
    FILTER_KABUPATEN = 'FILTER_KABUPATEN'
    FILTER_KECAMATAN = 'FILTER_KECAMATAN'
    OUTPUT_FOLDER = 'OUTPUT_FOLDER'
    OUTPUT_NAME = 'OUTPUT_NAME'

    URL_KABUPATEN = "https://geoservices.big.go.id/rbi/rest/services/BATASWILAYAH/Administrasi_AR_KabKota_50K/MapServer/0"
    URL_KECAMATAN = "https://geoservices.big.go.id/rbi/rest/services/BATASWILAYAH/Administrasi_AR_Kecamatan_10K/MapServer/0"
    URL_DESA      = "https://geoservices.big.go.id/rbi/rest/services/BATASWILAYAH/Administrasi_AR_KelDesa_10K/MapServer/0"

    TINGKAT_OPTIONS = [
        "Kabupaten/Kota (RBI 1:50K)",
        "Kecamatan (RBI 1:10K)",
        "Kelurahan/Desa (RBI 1:10K)"
    ]

    PROVINSI_LIST = sorted([
        "Aceh", "Bali", "Banten", "Bengkulu", "DKI Jakarta",
        "Daerah Istimewa Yogyakarta", "Gorontalo", "Jambi",
        "Jawa Barat", "Jawa Tengah", "Jawa Timur",
        "Kalimantan Barat", "Kalimantan Selatan", "Kalimantan Tengah",
        "Kalimantan Timur", "Kalimantan Utara",
        "Kepulauan Bangka Belitung", "Kepulauan Riau", "Lampung",
        "Maluku", "Maluku Utara", "Nusa Tenggara Barat", "Nusa Tenggara Timur",
        "Papua", "Papua Barat", "Papua Barat Daya", "Papua Pegunungan",
        "Papua Selatan", "Papua Tengah", "Riau",
        "Sulawesi Barat", "Sulawesi Selatan", "Sulawesi Tengah",
        "Sulawesi Tenggara", "Sulawesi Utara",
        "Sumatera Barat", "Sumatera Selatan", "Sumatera Utara"
    ])

    # Provinsi dropdown options: index 0 = "(Semua)", index 1+ = provinsi names
    PROVINSI_OPTIONS = ["(Semua / Kosongkan)"] + sorted([
        "Aceh", "Bali", "Banten", "Bengkulu", "DKI Jakarta",
        "Daerah Istimewa Yogyakarta", "Gorontalo", "Jambi",
        "Jawa Barat", "Jawa Tengah", "Jawa Timur",
        "Kalimantan Barat", "Kalimantan Selatan", "Kalimantan Tengah",
        "Kalimantan Timur", "Kalimantan Utara",
        "Kepulauan Bangka Belitung", "Kepulauan Riau", "Lampung",
        "Maluku", "Maluku Utara", "Nusa Tenggara Barat", "Nusa Tenggara Timur",
        "Papua", "Papua Barat", "Papua Barat Daya", "Papua Pegunungan",
        "Papua Selatan", "Papua Tengah", "Riau",
        "Sulawesi Barat", "Sulawesi Selatan", "Sulawesi Tengah",
        "Sulawesi Tenggara", "Sulawesi Utara",
        "Sumatera Barat", "Sumatera Selatan", "Sumatera Utara"
    ])

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterEnum(
            self.TINGKAT,
            'Tingkat Wilayah (Skala Data)',
            options=self.TINGKAT_OPTIONS,
            defaultValue=0
        ))

        self.addParameter(QgsProcessingParameterEnum(
            self.FILTER_PROVINSI,
            'Filter Provinsi (kosongkan = semua)',
            options=self.PROVINSI_OPTIONS,
            defaultValue=0,
            optional=True
        ))

        self.addParameter(QgsProcessingParameterString(
            self.FILTER_KABUPATEN,
            'Filter Kabupaten/Kota (ketik nama persis, kosongkan = semua)',
            optional=True,
            defaultValue=''
        ))

        self.addParameter(QgsProcessingParameterString(
            self.FILTER_KECAMATAN,
            'Filter Kecamatan (ketik nama persis, kosongkan = semua)',
            optional=True,
            defaultValue=''
        ))

        self.addParameter(QgsProcessingParameterFolderDestination(
            self.OUTPUT_FOLDER,
            'Output Folder'
        ))

        self.addParameter(QgsProcessingParameterString(
            self.OUTPUT_NAME,
            'Output Name (otomatis jika kosong)',
            optional=True,
            defaultValue=''
        ))

    # ------------------------------------------------------------------
    # Helpers
    # ------------------------------------------------------------------
    def _get_distinct(self, base_url, field, where="1=1"):
        """Query distinct field values from BIG REST service."""
        try:
            params = {
                "where": where,
                "outFields": field,
                "returnGeometry": "false",
                "returnDistinctValues": "true",
                "orderByFields": field,
                "f": "json"
            }
            resp = requests.get(f"{base_url}/query", params=params,
                                timeout=(10, 30))  # nosec
            resp.raise_for_status()
            data = resp.json()
            return sorted(set(
                (f["attributes"].get(field) or "").strip()
                for f in data.get("features", [])
                if f["attributes"].get(field)
            ))
        except Exception:
            return []

    def _query_all(self, base_url, where, feedback):
        """Download all features with pagination + retry."""
        all_features = []
        offset = 0
        batch = 1000
        max_retries = 3

        while True:
            if feedback.isCanceled():
                break

            params = {
                "where": where,
                "outFields": "*",
                "f": "geojson",
                "resultOffset": offset,
                "resultRecordCount": batch,
                "orderByFields": "objectid"
            }
            url = f"{base_url}/query"

            for attempt in range(1, max_retries + 1):
                try:
                    resp = requests.get(url, params=params,
                                        timeout=(15, 120))  # nosec
                    resp.raise_for_status()
                    data = resp.json()
                    break
                except Exception as e:
                    if attempt == max_retries:
                        raise Exception(
                            f"Gagal mengunduh batch offset={offset} "
                            f"setelah {max_retries} percobaan: {e}"
                        )
                    feedback.pushInfo(
                        f"  Percobaan {attempt}/{max_retries} gagal, "
                        "mencoba ulang..."
                    )
                    time.sleep(5 * attempt)

            batch_features = data.get("features", [])
            all_features.extend(batch_features)
            feedback.pushInfo(f"  Fetched {len(all_features)} features...")

            if len(batch_features) < batch:
                break
            offset += batch

        return {"type": "FeatureCollection", "features": all_features}

    # ------------------------------------------------------------------
    # Main Process
    # ------------------------------------------------------------------
    def processAlgorithm(self, parameters, context, feedback):
        # License check
        feedback.pushInfo("Checking license...")
        is_valid, msg, info = SupabaseGuard.check_license(
            "BIG Downloader QGIS", "free")
        feedback.pushInfo(msg)
        if not is_valid:
            raise Exception("License check failed.")

        # Read parameters
        tingkat_idx = self.parameterAsEnum(parameters, self.TINGKAT, context)
        prov_idx = self.parameterAsEnum(parameters, self.FILTER_PROVINSI, context)
        prov = self.PROVINSI_OPTIONS[prov_idx] if prov_idx > 0 else ""
        kab = (self.parameterAsString(
            parameters, self.FILTER_KABUPATEN, context) or "").strip()
        kec = (self.parameterAsString(
            parameters, self.FILTER_KECAMATAN, context) or "").strip()
        output_folder = self.parameterAsString(
            parameters, self.OUTPUT_FOLDER, context)
        output_name = (self.parameterAsString(
            parameters, self.OUTPUT_NAME, context) or "").strip()

        # Normalize level
        level_map = {0: "Kabupaten/Kota", 1: "Kecamatan", 2: "Kelurahan/Desa"}
        level = level_map.get(tingkat_idx, "Kabupaten/Kota")

        # Auto-generate output name if empty
        if not output_name:
            level_short = {
                "Kabupaten/Kota": "KK",
                "Kecamatan": "KC",
                "Kelurahan/Desa": "KD"
            }.get(level, "KK")
            parts = ["WADM", level_short]
            if prov:
                parts.append(re.sub(r'[^A-Za-z0-9]', '_', prov))
            if kab:
                parts.append(re.sub(r'[^A-Za-z0-9]', '_', kab))
            if kec:
                parts.append(re.sub(r'[^A-Za-z0-9]', '_', kec))
            output_name = "_".join(parts)

        safe_name = re.sub(r'[^A-Za-z0-9_]', '_', output_name)

        # Resolve URL and WHERE clause
        if level == "Kabupaten/Kota":
            svc_url = self.URL_KABUPATEN
            parts = ["1=1"]
            if prov:
                parts.append(f"WADMPR='{prov}'")
            where = " AND ".join(parts)
        elif level == "Kecamatan":
            svc_url = self.URL_KECAMATAN
            parts = ["1=1"]
            if prov:
                parts.append(f"WADMPR='{prov}'")
            if kab:
                parts.append(f"WADMKK='{kab}'")
            where = " AND ".join(parts)
        else:  # Kelurahan/Desa
            svc_url = self.URL_DESA
            parts = ["1=1"]
            if prov:
                parts.append(f"WADMPR='{prov}'")
            if kab:
                parts.append(f"WADMKK='{kab}'")
            if kec:
                parts.append(f"WADMKC='{kec}'")
            where = " AND ".join(parts)

        feedback.pushInfo("\n=== DOWNLOAD BATAS WILAYAH (BIG) ===")
        feedback.pushInfo(f"Tingkat   : {level}")
        feedback.pushInfo(f"Provinsi  : {prov or 'Semua'}")
        feedback.pushInfo(f"Kabupaten : {kab or '-'}")
        feedback.pushInfo(f"Kecamatan : {kec or '-'}")
        feedback.pushInfo("Mengunduh data dari server BIG...")

        # Download
        geojson_data = self._query_all(svc_url, where, feedback)
        total = len(geojson_data["features"])

        if total == 0:
            raise Exception(
                "Tidak ada data ditemukan dengan filter yang dipilih.")

        feedback.pushInfo(f"Total fitur: {total}")

        # Handle output folder
        if output_folder == 'TEMPORARY_OUTPUT':
            output_folder = os.path.join(
                tempfile.gettempdir(), 'BIG_BatasWilayah_Output')
        if not os.path.exists(output_folder):
            os.makedirs(output_folder, exist_ok=True)

        # Write GeoJSON to temp file
        tmp_json = os.path.join(output_folder, f"{safe_name}.geojson")
        with open(tmp_json, "w", encoding="utf-8") as f:
            json.dump(geojson_data, f, ensure_ascii=False)

        feedback.pushInfo("Konversi ke layer...")

        # Load as vector layer
        layer = QgsVectorLayer(tmp_json, safe_name, "ogr")
        if not layer.isValid():
            raise Exception("Gagal memuat GeoJSON sebagai layer.")

        # Save as GeoPackage
        gpkg_path = os.path.join(output_folder, f"{safe_name}.gpkg")
        error = QgsVectorFileWriter.writeAsVectorFormatV3(
            layer,
            gpkg_path,
            context.transformContext(),
            QgsVectorFileWriter.SaveVectorOptions()
        )

        # Cleanup temp geojson
        try:
            os.remove(tmp_json)
        except Exception:
                    pass  # nosec

        feedback.pushInfo(f"Berhasil! Data tersimpan di: {gpkg_path}")

        # Add to map
        if os.path.exists(gpkg_path):
            details = QgsProcessingContext.LayerDetails(
                safe_name, context.project(), safe_name)
            context.addLayerToLoadOnCompletion(gpkg_path, details)

        feedback.pushInfo("=== SELESAI ===")
        return {self.OUTPUT_FOLDER: output_folder}

    # ------------------------------------------------------------------
    # Metadata
    # ------------------------------------------------------------------
    def name(self):
        return 'download_batas_wilayah'

    def displayName(self):
        return '5. Download Batas Wilayah'

    def group(self):
        return 'BIG Downloader Tools'

    def groupId(self):
        return 'big_downloader_v2'

    def shortHelpString(self):
        return (
            "Download batas wilayah administrasi Indonesia "
            "(Kabupaten/Kota, Kecamatan, Kelurahan/Desa) "
            "dari layanan REST Badan Informasi Geospasial (BIG).\n\n"
            "Data dapat difilter per provinsi, kabupaten/kota, dan kecamatan.\n"
            "Kosongkan filter untuk mengunduh semua data."
        )

    def icon(self):
        from qgis.PyQt.QtGui import QIcon
        return QIcon(os.path.join(
            os.path.dirname(__file__), '..', 'icon.png'))

    def createInstance(self):
        return DownloadBatasWilayah()
