import os
import sys
import gc

import logging
import traceback
from typing import Protocol
import pandas as pd

from .rules import Rules
# ----------------------------
# Callback Function Signatures
# ----------------------------
class GetValidationResultsCallback(Protocol):
    def __call__(self, complete: bool = None, status_validation: bool = None, message: str = None, raport: pd.DataFrame = None) -> None: ...
class ZipStructRulesGet(Protocol):
    def __call__(self) -> dict[str, str] | str: ...
class GpkgStructRulesGet(Protocol):
    def __call__(self) -> dict[str, str] | str: ...
class GpkgDataRulesGet(Protocol):
    def __call__(self) -> dict[str, str] | str: ...
class UatLimitGet(Protocol):
    def __call__(self, siruta: str) -> dict[str, str] | str: ...
class GetProgressbarSteps(Protocol):
    def __call__(self, steps: int) -> None: ...
class GetProgressbarUpdate(Protocol):
    def __call__(self) -> None: ...
class GetProgressbarEnd(Protocol):
    def __call__(self, validation_status: bool) -> None: ...
# ----------------------------
# ----------------------------

# =============== Validation ===============
class Validation:
    def __init__(self, logger: logging.Logger):
        # Logger
        self.logger: logging.Logger = logger
        # Loop limit 
        self.loopLimit = 0
        
        # Validation rules
        self.validationRules: Rules = Rules(logger = self.logger, loop_limit = self.loopLimit)
    
    # ========== Validation ==========
    def validate(self, file_path: str, validation_package_version: str, 
            verification_results: dict[str, str],
            get_validation_results_callback: GetValidationResultsCallback,
            template_url: str, zip_struct_rules_get: ZipStructRulesGet, 
            gpkg_struct_rules_get: GpkgStructRulesGet, gpkg_data_rules_get: GpkgDataRulesGet,
            uat_limit_get: UatLimitGet, progressbar_steps: GetProgressbarSteps, progressbar_update: GetProgressbarUpdate, progressbar_end: GetProgressbarEnd):
        try:
            # Zip Validation
            if verification_results["FileType"] == "zip":
                self.validate_zip(file_path = file_path, validation_package_version = validation_package_version, 
                    verification_results = verification_results,
                    get_validation_results_callback = get_validation_results_callback,
                    template_url = template_url,
                    zip_struct_rules_get = zip_struct_rules_get,
                    gpkg_struct_rules_get = gpkg_struct_rules_get,
                    gpkg_data_rules_get = gpkg_data_rules_get,
                    uat_limit_get = uat_limit_get,
                    progressbar_steps = progressbar_steps,
                    progressbar_end = progressbar_end,
                    progressbar_update = progressbar_update)
            
            # Gpkg Validation
            elif verification_results["FileType"] == "gpkg":
                self.validate_gpkg(file_path = file_path, validation_package_version = validation_package_version,
                    verification_results = verification_results,
                    get_validation_results_callback = get_validation_results_callback,
                    template_url = template_url,
                    gpkg_struct_rules_get = gpkg_struct_rules_get,
                    gpkg_data_rules_get = gpkg_data_rules_get,
                    uat_limit_get = uat_limit_get,
                    progressbar_steps = progressbar_steps,
                    progressbar_end = progressbar_end,
                    progressbar_update = progressbar_update)
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.validate.__name__}] "
                f"Failed to validate the documentation. \n{traceBack}")
    # ================================
    
    # ========== Zip Archive Validation ==========
    def validate_zip(self, file_path: str, validation_package_version: str, 
            verification_results: dict[str, str], 
            get_validation_results_callback: GetValidationResultsCallback,
            template_url: str, zip_struct_rules_get: ZipStructRulesGet, 
            gpkg_struct_rules_get: GpkgStructRulesGet, gpkg_data_rules_get: GpkgDataRulesGet,
            uat_limit_get: UatLimitGet, progressbar_steps: GetProgressbarSteps, 
            progressbar_update: GetProgressbarUpdate, progressbar_end: GetProgressbarEnd):
        try:
            rulesZipStructure = zip_struct_rules_get()
            zipStructureSortedRules = sorted(rulesZipStructure["reguli_structura_zip"], key=lambda x: int(x["numar_regula"]))

            rulesGpkgStructure = gpkg_struct_rules_get()
            gpkgStructureSortedRules = sorted(rulesGpkgStructure["reguli_structura_gpkg"], key=lambda x: int(x["numar_regula"]))

            rulesGpkgData = gpkg_data_rules_get()
            gpkgDataSortedRules = sorted(rulesGpkgData["reguli_date_gpkg"], key=lambda x: int(x["numar_regula"]))

            progressbar_steps(steps = len(zipStructureSortedRules) + len(gpkgStructureSortedRules) + len(gpkgDataSortedRules) - 1)
            
            get_validation_results_callback(complete = False, message="Se valideaza arhiva ZIP...")
            
            # ===== Validate Zip Structure =====
            get_validation_results_callback(complete = False, message="Se valideaza structura arhivei ZIP...")
            
            statusZipStructure = self.validationRules.validate_zip_structure(
                rule_set = zipStructureSortedRules, file_path = file_path, plan_type = verification_results["PlanType"], progressbar_update = progressbar_update)
            
            if statusZipStructure == False:
                self.validationRules.rulesWriter.mark_validation_raport(template_version = verification_results["VersionTemplate"], plan_type = verification_results["PlanType"], validation_package_version = validation_package_version)
                get_validation_results_callback(complete = True, message="Structura arhivei ZIP nu este valida", status_validation = False, raport = self.validationRules.rulesWriter.raportDataFrame)
                progressbar_end(validation_status = False)
                return
            else:
                get_validation_results_callback(complete = False, message="Structura arhivei ZIP este valida.")
                
            # ==================================
            
            # ===== Validate Gpkg Structure =====
            get_validation_results_callback(complete = False, message="Se valideaza structura fisierului GPKG...")
            
            statusGpkgStructure = self.validationRules.validate_gpkg_structure(
                rule_set = gpkgStructureSortedRules[0], file_path = file_path, document_template_url = template_url, plan_type = verification_results["PlanType"], progressbar_update = progressbar_update)
            
            if statusGpkgStructure == False:
                self.validationRules.rulesWriter.mark_validation_raport(template_version = verification_results["VersionTemplate"], plan_type = verification_results["PlanType"], validation_package_version = validation_package_version)
                get_validation_results_callback(complete = True, message="Structura fisierului GPKG nu este valida", status_validation = False, raport = self.validationRules.rulesWriter.raportDataFrame)
                progressbar_end(validation_status = False)
                return
            else:
                get_validation_results_callback(complete = False, message="Structura fisierului GPKG este valida.")
                
            # ===================================
            
            # ===== Validate Gpkg Data =====
            get_validation_results_callback(complete = False, message="Se valideaza datele fisierului GPKG...")
            
            statusGpkgData = self.validationRules.validate_gpkg_data(
                rule_set = gpkgDataSortedRules, uat_limit_get = uat_limit_get, plan_type = verification_results["PlanType"], progressbar_update = progressbar_update)
            
            if statusGpkgData == False:
                self.validationRules.rulesWriter.mark_validation_raport(template_version = verification_results["VersionTemplate"], plan_type = verification_results["PlanType"], validation_package_version = validation_package_version)
                get_validation_results_callback(complete = True, message="Datele fisierului GPKG nu sunt valide", status_validation = False, raport = self.validationRules.rulesWriter.raportDataFrame)
                progressbar_end(validation_status = False)
                return
            else:
                get_validation_results_callback(complete = False, message="Datele fisierului GPKG sunt valide.")
            
            progressbar_end(validation_status = True)
            self.validationRules.rulesWriter.mark_validation_raport(template_version = verification_results["VersionTemplate"], plan_type = verification_results["PlanType"], validation_package_version = validation_package_version)
            get_validation_results_callback(complete = True, message="Arhiva zip a fost validata.", status_validation = True, raport = self.validationRules.rulesWriter.raportDataFrame)
            
            # ==============================
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.validate_zip.__name__}] "
                f"Failed to validate the zip archive. \n{traceBack}")
    # ============================================
    
    # ========== Gpkg File Validation ==========
    def validate_gpkg(self, file_path: str, validation_package_version: str, 
            verification_results: dict[str, str], 
            get_validation_results_callback: GetValidationResultsCallback,
            template_url: str, gpkg_struct_rules_get: GpkgStructRulesGet, gpkg_data_rules_get: GpkgDataRulesGet,
            uat_limit_get: UatLimitGet, progressbar_steps: GetProgressbarSteps, 
            progressbar_update: GetProgressbarUpdate, progressbar_end: GetProgressbarEnd):
        try:
            rulesGpkgStructure = gpkg_struct_rules_get()
            gpkgStructureSortedRules = sorted(rulesGpkgStructure["reguli_structura_gpkg"], key=lambda x: int(x["numar_regula"]))

            rulesGpkgData = gpkg_data_rules_get()
            gpkgDataSortedRules = sorted(rulesGpkgData["reguli_date_gpkg"], key=lambda x: int(x["numar_regula"]))

            progressbar_steps(steps = len(gpkgStructureSortedRules) + len(gpkgDataSortedRules) - 1)
            get_validation_results_callback(complete = False, message="Se valideaza fisierul GPKG...")
            
            # ===== Validate Gpkg Structure =====
            get_validation_results_callback(complete = False, message="Se valideaza structura fisierului GPKG...")
            
            statusGpkgStructure = self.validationRules.validate_gpkg_structure(
                rule_set = gpkgStructureSortedRules[0], file_path = file_path, document_template_url = template_url, plan_type = verification_results["PlanType"], progressbar_update = progressbar_update)
            
            if statusGpkgStructure == False:
                self.validationRules.rulesWriter.mark_validation_raport(template_version = verification_results["VersionTemplate"], plan_type = verification_results["PlanType"], validation_package_version = validation_package_version)
                get_validation_results_callback(complete = True, message="Structura fisierului GPKG nu este valida", status_validation = False, raport = self.validationRules.rulesWriter.raportDataFrame)
                progressbar_end(validation_status = False)
                return
            else:
                get_validation_results_callback(complete = False, message="Structura fisierului GPKG este valida.")
                
            # ===================================
            
            # ===== Validate Gpkg Data =====
            get_validation_results_callback(complete = False, message="Se valideaza datele fisierului GPKG...")
            
            statusGpkgData = self.validationRules.validate_gpkg_data(
                rule_set = gpkgDataSortedRules, uat_limit_get = uat_limit_get, plan_type = verification_results["PlanType"], progressbar_update = progressbar_update)
            
            if statusGpkgData == False:
                self.validationRules.rulesWriter.mark_validation_raport(template_version = verification_results["VersionTemplate"], plan_type = verification_results["PlanType"], validation_package_version = validation_package_version)
                get_validation_results_callback(complete = True, message="Datele fisierului GPKG nu sunt valide", status_validation = False, raport = self.validationRules.rulesWriter.raportDataFrame)
                progressbar_end(validation_status = False)
                return
            else:
                get_validation_results_callback(complete = False, message="Datele fisierului GPKG sunt valide.")
            
            progressbar_end(validation_status = True)
            self.validationRules.rulesWriter.mark_validation_raport(template_version = verification_results["VersionTemplate"], plan_type = verification_results["PlanType"], validation_package_version = validation_package_version)
            get_validation_results_callback(complete = True, message="Arhiva zip a fost validata.", status_validation = True, raport = self.validationRules.rulesWriter.raportDataFrame)
            
            # ==============================
            pass
        except:
            traceBack = traceback.format_exc()
            self.logger.error(
                f"[{self.__class__.__name__} - {self.validate_gpkg.__name__}] "
                f"Failed to validate the gpkg file. \n{traceBack}")
    # ==========================================
# ==========================================