# -*- coding: utf-8 -*-
import os
import urllib.request
import urllib.parse
import urllib.error
import ssl
import time
import tempfile
import shutil
import re
from qgis.core import (
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingParameterFeatureSource,
    QgsProcessingParameterString,
    QgsProcessingParameterFolderDestination,
    QgsProcessingParameterBoolean,
    QgsProcessingParameterCrs,
    QgsGeometry,
    QgsCoordinateTransform
)
from qgis import processing
from .license_guard import SupabaseGuard

class DownloadBATNAS(QgsProcessingAlgorithm):
    INPUT_GRID = 'INPUT_GRID'
    TOKEN = 'TOKEN'
    OUTPUT_FOLDER = 'OUTPUT_FOLDER'
    OUTPUT_CRS = 'OUTPUT_CRS'
    DATA_VERSION = 'DATA_VERSION'
    CREATE_MOSAIC = 'CREATE_MOSAIC'

    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterFeatureSource(
            self.INPUT_GRID, 'Input Grid Features', [QgsProcessing.TypeVectorPolygon]
        ))
        
        self.addParameter(QgsProcessingParameterString(
            self.TOKEN, 'API Token'
        ))
        
        self.addParameter(QgsProcessingParameterFolderDestination(
            self.OUTPUT_FOLDER, 'Output Folder'
        ))
        
        self.addParameter(QgsProcessingParameterCrs(
            self.OUTPUT_CRS, 'Output Coordinate System', optional=True
        ))
        
        self.addParameter(QgsProcessingParameterString(
            self.DATA_VERSION, 'Data Version', defaultValue='1.1'
        ))
        
        self.addParameter(QgsProcessingParameterBoolean(
            self.CREATE_MOSAIC, 'Create Mosaic of All Tiles', defaultValue=True
        ))

    def clean_raster_name(self, name):
         clean = re.sub(r'_v\d+\.\d+\.tif$', '', name, flags=re.IGNORECASE)
         clean = re.sub(r'[^a-zA-Z0-9]', '_', clean)
         return clean[:50]

    def processAlgorithm(self, parameters, context, feedback):
        # 1. 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.")

        input_source = self.parameterAsSource(parameters, self.INPUT_GRID, context)
        token = self.parameterAsString(parameters, self.TOKEN, context)
        output_folder = self.parameterAsString(parameters, self.OUTPUT_FOLDER, context)
        output_crs = self.parameterAsCrs(parameters, self.OUTPUT_CRS, context)
        data_version = self.parameterAsString(parameters, self.DATA_VERSION, context)
        create_mosaic = self.parameterAsBool(parameters, self.CREATE_MOSAIC, context)

        # Handle Temporary Output
        if output_folder == 'TEMPORARY_OUTPUT':
             output_folder = os.path.join(tempfile.gettempdir(), 'BIG_BATNAS_Output')
             if not os.path.exists(output_folder):
                 os.makedirs(output_folder)
             feedback.pushInfo(f"Using temporary folder: {output_folder}")

        # 2. Iterate Grid
        downloaded = []
        ctx = ssl.create_default_context()
        ctx.check_hostname = False
        ctx.verify_mode = ssl.CERT_NONE
        
        # Create a specific temp dir for tiles to avoid polluting output
        tiles_temp_dir = tempfile.mkdtemp(prefix='batnas_tiles_')
        feedback.pushInfo(f"Downloading tiles to temporary folder: {tiles_temp_dir}")
        
        total_feats = input_source.featureCount()
        current = 0
        
        try:
            for feat in input_source.getFeatures():
                if feedback.isCanceled(): break
                current += 1
                feedback.setProgress(int((current/total_feats)*100))
                
                # Assuming field "Penamaan" or "Name"
                idx = feat.fieldNameIndex("Penamaan")
                if idx == -1: idx = feat.fieldNameIndex("Name")
                
                if idx == -1:
                    feedback.reportError(f"Field 'Penamaan' or 'Name' not found for feature {feat.id()}")
                    continue
                    
                grid_name = feat.attributes()[idx]
                filename = f"BATNAS_{grid_name}_MSL_v{data_version}.tif"
                clean = self.clean_raster_name(filename)
                
                # Save to TEMP dir first
                temp_path = os.path.join(tiles_temp_dir, f"{clean}.tif")
                
                url = f"https://tanahair.indonesia.go.id/api-inageo/unduh/batnas?token={token}&filename={urllib.parse.quote(filename)}"
                
                # feedback.pushInfo(f"Downloading {filename}...")
                
                # Retry Logic for Download
                max_retries = 3
                success = False
                
                for attempt in range(max_retries):
                    try:
                        req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
                        with urllib.request.urlopen(req, context=ctx, timeout=300) as response:
                            with open(temp_path, 'wb') as f:
                                f.write(response.read())
                                
                        if os.path.exists(temp_path) and os.path.getsize(temp_path) > 0:
                            downloaded.append(temp_path)
                            feedback.pushInfo(f"Berhasil: {clean}")
                            success = True
                            break
                        else:
                            # Empty file, maybe retry?
                            if attempt < max_retries - 1:
                                time.sleep(2)
                                continue
                            else:
                                feedback.reportError(f"Gagal (File Kosong): {filename}")
                            
                    except (urllib.error.HTTPError, urllib.error.URLError, Exception) as e:
                         # Specific HTTP Errors
                         if isinstance(e, urllib.error.HTTPError) and e.code == 401:
                             raise Exception("HTTP Error 401: Unauthorized. Token API Anda mungkin kadaluarsa.")
                         
                         if attempt < max_retries - 1:
                             feedback.pushInfo(f"Gagal {filename} (Attempt {attempt+1}/{max_retries}): {str(e)}. Retrying...")
                             time.sleep(2)
                         else:
                             feedback.reportError(f"Gagal {filename}: {str(e)}")

                if not success:
                    # Decide if we stop or continue? 
                    # User requested if one fails, maybe warn? But for mosaic, missing tiles is bad.
                    # But previous log showed continue.
                    pass
    
            if not downloaded:
                raise Exception("Tidak ada file yang berhasil diunduh.")
    
            # 3. Process Output
            if create_mosaic:
                feedback.pushInfo("\n=== MEMBUAT MOSAIK ===")
                
                # Fix: Ensure output folder exists (handling obscure QGIS temp paths)
                if not os.path.exists(output_folder):
                    try: os.makedirs(output_folder, exist_ok=True)
                    except: pass
                    
                mosaic_out = os.path.join(output_folder, "BATNAS_Mosaic.tif")
                
                # Use GDAL Warp directly to ensure file locks are released (User Request)
                # processing.run("gdal:merge"...) holds locks sometimes.
                from osgeo import gdal
                
                feedback.pushInfo(f"Merging {len(downloaded)} tiles using GDAL Warp...")
                
                try:
                    # Define options: Float32, NoData -9999
                    warp_opts = gdal.WarpOptions(
                        format='GTiff',
                        outputType=gdal.GDT_Float32,
                        dstNodata=-9999,
                        resampleAlg=gdal.GRA_Bilinear
                    )
                    
                    # Run Warp (Mosaic)
                    # Returns a Dataset object, we must set it to None to close it and release locks!
                    ds = gdal.Warp(mosaic_out, downloaded, options=warp_opts)
                    ds = None # CRITICAL: Releases file locks on inputs and output
                    
                except Exception as e:
                    raise QgsProcessingException(f"Gagal membuat mosaik dengan GDAL Warp: {e}")
                
                # Reproject if requested
                if output_crs.isValid():
                    feedback.pushInfo(f"Reprojecting mosaic to {output_crs.authid()}...")
                    proj_out = os.path.join(output_folder, "BATNAS_Mosaic_Proj.tif")
                    
                    # Ensure folder exists
                    if not os.path.exists(os.path.dirname(proj_out)):
                        try: os.makedirs(os.path.dirname(proj_out), exist_ok=True)
                        except: pass
                        
                    warp_params = {
                        'INPUT': mosaic_out,
                        'TARGET_CRS': output_crs,
                        'NODATA': -9999,
                        'OUTPUT': proj_out
                    }
                    processing.run("gdal:warpreproject", warp_params, context=context, feedback=feedback)
                    
                    if os.path.exists(mosaic_out):
                        try: os.remove(mosaic_out)
                        except: pass
                    mosaic_out = proj_out

                feedback.pushInfo(f"Mosaic berhasil dibuat: {mosaic_out}")
                
                # Explicitly load the layer since FolderDestination doesn't auto-load rasters
                if os.path.exists(mosaic_out):
                    from qgis.core import QgsProcessingContext
                    details = QgsProcessingContext.LayerDetails("BATNAS Mosaic", context.project(), "BATNAS_Mosaic")
                    context.addLayerToLoadOnCompletion(mosaic_out, details)
                
                return {self.OUTPUT_FOLDER: mosaic_out}
            
            else:
                feedback.pushInfo("\n=== MENYIMPAN FILE ===")
                if not os.path.exists(output_folder):
                    try: os.makedirs(output_folder, exist_ok=True)
                    except: pass
                    
                final_files = []
                for t_path in downloaded:
                    fname = os.path.basename(t_path)
                    out_path = os.path.join(output_folder, fname)
                    
                    if output_crs.isValid():
                         warp_params = {
                             'INPUT': t_path,
                             'TARGET_CRS': output_crs,
                             'NODATA': -9999,
                             'OUTPUT': out_path
                         }
                         processing.run("gdal:warpreproject", warp_params, context=context, feedback=feedback)
                    else:
                         shutil.copy2(t_path, out_path)
                    final_files.append(out_path)
                    
                    # Also load these if not mosaic?
                    # The user usually wants to see what they downloaded.
                    if os.path.exists(out_path):
                        from qgis.core import QgsProcessingContext
                        details = QgsProcessingContext.LayerDetails(fname, context.project(), fname)
                        context.addLayerToLoadOnCompletion(out_path, details)
                    
                return {self.OUTPUT_FOLDER: output_folder}
                
        finally:
            # CLEANUP TEMP FILES
             if os.path.exists(tiles_temp_dir):
                 # Stronger cleanup loop
                 # Try 5 times with increasing delays
                 for i in range(5):
                     try:
                         shutil.rmtree(tiles_temp_dir, ignore_errors=True)
                         if not os.path.exists(tiles_temp_dir):
                             break
                         time.sleep(1 + i) # 1s, 2s, 3s...
                     except:
                         time.sleep(1)

    def name(self):
        return 'download_batnas'

    def displayName(self):
        return '4. Download BATNAS'

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

    def groupId(self):
        return 'big_downloader_v2'

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

    def createInstance(self):
        return DownloadBATNAS()
