from functools import wraps
from django.http import HttpResponseForbidden
from django.shortcuts import redirect
from .models import Users
from ftplib import FTP
import logging
import telebot
import requests
from typing import Dict, Any, Optional, List
import xml.etree.ElementTree as ET


class TelegramLog:
    def __init__(self,BOT_TOKEN="6723497869:AAEKCB3YJUGnUFeMihLlr-FHblTuvtoVUZA",GROUP_ID='-4155125438'):
        self.BOT_TOKEN = BOT_TOKEN
        self.GROUP_ID = GROUP_ID

        
    # Send telegram ad replacement time execution 
    def send_telegram_log(self,message):
        bot = telebot.TeleBot(self.BOT_TOKEN)
        bot.send_message(self.GROUP_ID, message)

def check_user(func):
    @wraps(func)
    def wrapper(request, *args, **kwargs):
        # Your custom logic based on the request parameter
        if ('id_user' in request.session) and (request.session['id_user']) and (request.session['id_user'] != None) and Users.objects.filter(id_user=request.session['id_user']) != None :
            # Perform some checks or actions
            return func(request, *args, **kwargs)
        else:
            return redirect("login") 
    return wrapper


class NoVastResult:
    def __init__(self, id_campaign, total_ads, total_impressions):
        self.id_campaign = id_campaign
        self.total_ads = total_ads
        self.total_impressions = int(total_impressions)

class VastResult:
    def __init__(self, id_campaign, total_ads, total_impressions):
        self.id_campaign = id_campaign
        self.total_ads = total_ads
        self.total_impressions = int(total_impressions)

class FTPClient:

    def __init__(self, ftp_server, port=21, username=None, password=None):
        self.ftp_server = ftp_server
        self.port = port
        self.username = username
        self.password = password
        self.logger = self._setup_logger()

    def _setup_logger(self):
        logger = logging.getLogger(__name__)
        logger.setLevel(logging.INFO)
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
        ch = logging.StreamHandler()
        ch.setFormatter(formatter)
        logger.addHandler(ch)
        return logger

    def connect(self):
        self.ftp = FTP()
        self.ftp.connect(self.ftp_server, self.port)
        self.ftp.login(self.username, self.password)

    def disconnect(self):
        if hasattr(self, 'ftp'):
            self.ftp.quit()

    def upload_file(self, local_file_path, remote_dir):
        try:
            self.connect()
            self.ftp.cwd(remote_dir)
            filename = local_file_path.split('/')[-1]
            with open(local_file_path, 'rb') as local_file:
                self.ftp.storbinary(f'STOR {filename}', local_file)
            self.logger.info(f"File '{filename}' uploaded successfully to '{remote_dir}'")
            return True
        except Exception as e:
            self.logger.error(f"Failed to upload file: {e}")
            return False
        finally:
            self.disconnect()


def calculate_priority_score(TSA, ARF, U, P, W_TSA=0.4, W_ARF=0.2, W_U=0.3, W_P=0.1):
    """
    Calculate the priority score for a campaign.
    
    Args:
    - TSA (float): Target Show Alignment value (between 0 and 1).
    - ARF (float): Airing Requirement Fulfillment value (between 0 and 1).
    - U (float): Urgency value (between 0 and 1).
    - P (float): Position value (between 0 and 1).
    - W_TSA (float): Weight for Target Show Alignment (default is 0.4).
    - W_ARF (float): Weight for Airing Requirement Fulfillment (default is 0.2).
    - W_U (float): Weight for Urgency (default is 0.3).
    - W_P (float): Weight for Position (default is 0.1).
    
    Returns:
    - float: Priority score for the campaign.
    """ 
    return (W_TSA * TSA) + (W_ARF * (1 - ARF)) + (W_U * U) + (W_P * P)


from ftplib import FTP, error_perm

class FTPConnector:
    """
    A class to manage FTP connections and operations.

    Attributes:
        host (str): The FTP server hostname.
        port (int): The port number of the FTP server.
        username (str): The username for authentication.
        password (str): The password for authentication.
        ftp (FTP): An instance of the FTP class for FTP operations.
    """

    def __init__(self, host, port=None, username=None, password=None):
        """
        Initializes the FTPConnector with server details.

        Args:
            host (str): The FTP server hostname.
            port (int, optional): The port number of the FTP server. Defaults to None.
            username (str, optional): The username for authentication. Defaults to None.
            password (str, optional): The password for authentication. Defaults to None.
        """
        self.host = host
        self.port = port
        self.username = username
        self.password = password
        self.ftp = None
    
    def connect(self):
        """
        Connects to the FTP server.

        Raises:
            Exception: If connection fails.
        """
        try:
            if self.port:
                self.ftp = FTP()
                self.ftp.connect(self.host, self.port)
            else:
                self.ftp = FTP(self.host)
            self.ftp.login(self.username, self.password)
            print("Connected to FTP server.")
        except Exception as e:
            print(f"Error connecting to FTP server: {e}")

    def disconnect(self):
        """
        Disconnects from the FTP server.

        Raises:
            Exception: If disconnection fails.
        """
        try:
            if self.ftp:
                self.ftp.quit()
                print("Disconnected from FTP server.")
        except Exception as e:
            print(f"Error disconnecting from FTP server: {e}")

    def list_files(self):
        """
        Lists files on the FTP server.
        """
        try:
            self.connect()
            if self.ftp:
                files = self.ftp.nlst()
                print("List of files on server:")
                for file in files:
                    print(file)
            else:
                print("Not connected to FTP server.")
        except Exception as e:
            print(f"Error listing files: {e}")
        finally:
            self.disconnect()

    def upload_file(self, local_path, remote_path):
        """
        Uploads a file to the FTP server.

        Args:
            local_path (str): The local path of the file to upload.
            remote_path (str): The remote path where the file will be uploaded.

        Raises:
            Exception: If file upload fails.
        """
        try:
            self.connect()
            if self.ftp:
                # with open(local_path, 'rb') as file:
                #     self.ftp.storbinary(f'STOR {remote_path}', file)
            
                with open(local_path, 'rb') as local_file:
                    remote_filename = local_path.split('/')[-1]  # Extract filename from local file path
                    self.ftp.cwd(remote_path)  # Change to remote directory
                    self.ftp.storbinary(f"STOR {remote_filename}", local_file)  # Upload file
                    
                print(f"Uploaded {local_path} to {remote_path}.")
            else:
                print("Not connected to FTP server.")
        except Exception as e:
            print(f"Error uploading file: {e}")
        finally:
            self.disconnect()

    def download_file(self, remote_path, local_path):
        """
        Downloads a file from the FTP server to the local machine.

        Args:
            remote_path (str): The remote path of the file on the server.
            local_path (str): The local path where the file will be saved.

        Raises:
            Exception: If file download fails.
        """
        try:
            self.connect()
            if self.ftp:
                with open(local_path, 'wb') as file:
                    self.ftp.retrbinary(f'RETR {remote_path}', file.write)
                print(f"Downloaded {remote_path} to {local_path}.")
            else:
                print("Not connected to FTP server.")
        except Exception as e:
            print(f"Error downloading file: {e}")
        finally:
            self.disconnect()

    def create_directory(self, directory):
        """
        Creates a directory on the FTP server.

        Args:
            directory (str): The directory path to be created.

        Raises:
            Exception: If directory creation fails.
        """
        try:
            self.connect()
            if self.ftp:
                self.ftp.mkd(directory)
                print(f"Created directory: {directory}.")
            else:
                print("Not connected to FTP server.")
        except Exception as e:
            print(f"Error creating directory: {e}")
        finally:
            self.disconnect()

    def delete_file(self, remote_path):
        """
        Deletes a file from the FTP server.

        Args:
            remote_path (str): The remote path of the file to be deleted.

        Raises:
            Exception: If file deletion fails.
        """
        try:
            self.connect()
            if self.ftp:
                self.ftp.delete(remote_path)
                print(f"Deleted file: {remote_path}.")
            else:
                print("Not connected to FTP server.")
        except Exception as e:
            print(f"Error deleting file: {e}")
        finally:
            self.disconnect()

    def delete_directory(self, directory):
        """
        Deletes a directory from the FTP server.

        Args:
            directory (str): The directory path to be deleted.

        Raises:
            Exception: If directory deletion fails.
        """
        try:
            self.connect()
            if self.ftp:
                self.ftp.rmd(directory)
                print(f"Deleted directory: {directory}.")
            else:
                print("Not connected to FTP server.")
        except Exception as e:
            print(f"Error deleting directory: {e}")
        finally:
            self.disconnect()

    def rename_file(self, from_name, to_name):
        """
        Renames a file on the FTP server.

        Args:
            from_name (str): The current name of the file.
            to_name (str): The new name for the file.

        Raises:
            Exception: If file renaming fails.
        """
        try:
            self.connect()
            if self.ftp:
                self.ftp.rename(from_name, to_name)
                print(f"Renamed file from {from_name} to {to_name}.")
            else:
                print("Not connected to FTP server.")
        except Exception as e:
            print(f"Error renaming file: {e}")
        finally:
            self.disconnect()



def hhmmssf_to_seconds(time_str):
    # Split the time string into hours, minutes, seconds, and microseconds
    hours, minutes, seconds, microseconds = map(int, time_str.split(':'))

    # Calculate the total number of seconds
    total_seconds = hours * 3600 + minutes * 60 + seconds + microseconds / 1000000
    
    return total_seconds


# Function to make requests asynchronously
def vast_update_requests(url,reply):

    from urllib.parse import urlparse, parse_qs
    parsed_url = urlparse(url)
    headers = {
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
    }

    # Get the query parameters
    query_params = parse_qs(parsed_url.query)
    response = requests.get(url=url,headers=headers)
    if response.text != None:
        print(response.text)
        # Get the value of the 'event' parameter
        event_value = query_params.get('event')

        if "js_start" in url :
            # print(reply)
            reply.tracking_start_status = response.text
            reply.save()
        elif "js_first_quartile" in url:
            reply.tracking_firstquartile_status = response.text
            reply.save()
        elif "js_midpoint" in url:
            reply.tracking_midpoint_status = response.text
            reply.save()
        elif "js_third_quartile" in url:
            reply.tracking_thirdquartile_status = response.text
            reply.save()
        elif "vast_impression" in url:
            reply.impression_sprintserve_status = response.text
            reply.save()
        elif "doubleclick" in url:
            reply.impression_double_click_status = response.text
            reply.save()
        elif "js_complete" in url:
            print("Check: ",response.text)
            reply.tracking_completed_status = response.text
            reply.save()

        else:

            reply.impression_double_click_status = response.text
            reply.save()
        return response


def visioner_playlist():

    from datetime import datetime, timedelta
    """
    return all playlist ads
    input: almatv url,
    output: list of dictionaries contain (playlit info, playlist date,last week time, script start time)
    """
    url = "https://almatv-stor.vizionr.fr/synthesia/almatv/playlist/manager/php/_getPlaylist.php?fields=title,isPub,duration"
    response = requests.get(url)
    if response.status_code == 200:
        head_data = response.json()[0]
        playlist_data = response.json()[0]['data']
        
        # Generate data for DAI api
        processed_data = [ {**item} for item in playlist_data if "isPub" in item and item["isPub"] == '1' ]

        return processed_data
    else:
        return None


def fetch_xml_from_url(url: str) -> Optional[str]:
    """
    Fetch XML content from a URL and check status code.
    
    Args:
        url: The URL to send the request to
        
    Returns:
        The XML content as string if successful, None otherwise
    """
    try:
        response = requests.get(url, timeout=30)
        response.raise_for_status()  # Raise an exception for 4XX/5XX responses
        
        if response.content:
            print(f"Successfully fetched content from {url}")
            return response.text
        else:
            print(f"No content received from {url}")
            return None
            
    except requests.exceptions.RequestException as e:
        print(f"Error fetching URL {url}: {str(e)}")
        return None

def parse_xml_content(xml_content: str) -> Optional[ET.Element]:

    """
    Parse XML content into an ElementTree.
    
    Args:
        xml_content: String containing XML data
        
    Returns:
        The root element of the parsed XML, or None if parsing failed
    """
    try:
        root = ET.fromstring(xml_content)
        print("Successfully parsed XML content")
        return root
    except ET.ParseError as e:
        print(f"Failed to parse XML: {str(e)}")
        return None

def extract_data_by_attributes(root: ET.Element, tag_name: str, attributes: Dict[str, str] = None) -> List[Dict[str, Any]]:
    """
    Extract data from XML elements based on tag name and optional attributes.
    
    Args:
        root: The root XML element
        tag_name: The tag to search for
        attributes: Optional dictionary of attribute names and values to match
                   If None, all elements with the tag_name will be returned
        
    Returns:
        List of dictionaries containing the matched elements' data
    """
    results = []
    elements_root = []
    
    # Find all elements with the specified tag
    elements = root.findall(f".//{tag_name}")
    print(f"Found {len(elements)} elements with tag '{tag_name}'")
    
    for element in elements:
        # If attributes is None or empty, accept all elements with the tag
        # Otherwise, check if all required attributes match
        matches_criteria = True
        
        if attributes:
            for attr_name, attr_value in attributes.items():
                if element.get(attr_name) != attr_value:
                    matches_criteria = False
                    break
                
        if matches_criteria:
            # Extract data from the matching element
            element_data = {
                "text": element.text,
                "attributes": {k: v for k, v in element.attrib.items()},
                "children": []
            }
            
            # Process children
            for child in element:
                child_data = {
                    "tag": child.tag,
                    "text": child.text,
                    "attributes": {k: v for k, v in child.attrib.items()}
                }
                element_data["children"].append(child_data)
                
            results.append(element_data)
            elements_root.append(element)
    
    if attributes:
        print(f"Extracted {len(results)} elements matching attribute criteria")
    else:
        print(f"Extracted {len(results)} elements with tag '{tag_name}'")
    
    return elements_root, results


# VAST CALL to ALMA
def call_vast_api():
    from .tasks import call_vast
    import requests
    from requests.auth import HTTPProxyAuth
    from concurrent.futures import ThreadPoolExecutor
    from urllib3.util import parse_url
    import random
    import datetime
    import os
    import xml.etree.ElementTree as ET

    iab_cat = {
        'Feuilleton': 500001 ,
        'Magazine': 500005,
        'Mini-Série': 500001,
        'Magazine ': 500005,
        'Mini-Serie': 500001,
        'Serie': 500001,
        'Information': 500011,
        'Magazine Sportif': 500013,
        'Dessin Anime': 500004,
        'Magazine Sportif ': 500013,
        'Telefilm': 500001,
        'Documentaire': 500003,
        'Religieux': 500008,
        'Sport': 500013,
        'Long Metrage': 500002,
        'News': 500011,
        'Long-Metrage': 500002,
        'Des Histoires Et Des Hommes': 500001,
        'Série': 500001,
        'Musique': 500006,
        'Theatre': 500007,
        'Spectacle': 500012,
        'Jeux': 500014
    }

    # iab_c = iab_cat[genre]

    params = {
        'w': '720',
        'h': '567',
        'content_genre': "Dessin Anime",
        'content_title': "Abtal albihar",
        'language': 'ar-MA',
        'pod_max_dur': 20,
        'channel_name': '2M_TV',
        'country': 'France'

    }

    # num_requests = total_volume
    num_requests = 20
    # *Links to send you have to comment them one by one

    #! Weaber
    # url = "https://tv.springserve.com/vast/769609"

    #! City
    # url = 'https://tv.springserve.com/vast/850575?w=1920&h=1080&cb={{CACHEBUSTER}}&ip={{IP}}&ua={{USER_AGENT}}&pod_max_dur={{POD_MAX_DUR}}&pod_ad_slots={{POD_AD_SLOTS}}&app_bundle={{APP_BUNDLE}}&app_name={{APP_NAME}}&app_store_url={{APP_STORE_URL}}&did={{DEVICE_ID}}&us_privacy={{US_PRIVACY}}'

    #! Ninjago
    url = "https://tv.springserve.com/vast/850576?w=1920&h=1080&cb={{CACHEBUSTER}}&ip={{IP}}&ua={{USER_AGENT}}&pod_max_dur={{POD_MAX_DUR}}&pod_ad_slots={{POD_AD_SLOTS}}&app_bundle={{APP_BUNDLE}}&app_name={{APP_NAME}}&app_store_url={{APP_STORE_URL}}&did={{DEVICE_ID}}&us_privacy={{US_PRIVACY}}"

    #! Technic
    # url = "https://tv.springserve.com/vast/850577?w=1920&h=1080&cb={{CACHEBUSTER}}&ip={{IP}}&ua={{USER_AGENT}}&pod_max_dur={{POD_MAX_DUR}}&pod_ad_slots={{POD_AD_SLOTS}}&app_bundle={{APP_BUNDLE}}&app_name={{APP_NAME}}&app_store_url={{APP_STORE_URL}}&did={{DEVICE_ID}}&us_privacy={{US_PRIVACY}}"

    #! Star Wars
    # url = "https://tv.springserve.com/vast/852369?w=1920&h=1080&cb={{CACHEBUSTER}}&ip={{IP}}&ua={{USER_AGENT}}&pod_max_dur={{POD_MAX_DUR}}&pod_ad_slots={{POD_AD_SLOTS}}&app_bundle={{APP_BUNDLE}}&app_name={{APP_NAME}}&app_store_url={{APP_STORE_URL}}&did={{DEVICE_ID}}&us_privacy={{US_PRIVACY}}"

    headers = {
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
    }
    vast = call_vast.delay(url=url,num_requests=num_requests,params=params)


    proxy_list = [
        # {"http": "http://91.208.69.128:12323", "https": "https://91.208.69.128:12323", "auth": HTTPProxyAuth("14ae3c1adf82b", "c66b1ace34")},
        # {"http": "http://45.149.167.59:12323", "https": "https://45.149.167.59:12323", "auth": HTTPProxyAuth("14ae3c1adf82b", "c66b1ace34")},
        # {"http": "http://191.101.246.245:12323", "https": "https://191.101.246.245:12323", "auth": HTTPProxyAuth("14ae3c1adf82b", "c66b1ace34")},
        # {"http": "http://191.101.246.108:12323", "https": "https://191.101.246.108:12323", "auth": HTTPProxyAuth("14ae3c1adf82b", "c66b1ace34")},
        # {"http": "http://185.248.48.237:12323", "https": "https://185.248.48.237:12323", "auth": HTTPProxyAuth("14ae3c1adf82b", "c66b1ace34")},


        # {"http": "185.124.241.179:12323", "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},
        # {"http": "89.116.242.185:12323", "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},
        # {"http": "45.140.244.198:12323", "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},
        # {"http": "193.106.198.128:12323", "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},
        # {"http": "136.175.224.210:12323", "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},

        {"http": "185.124.241.179:12323",  "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},
        {"http": "89.116.242.185:12323",   "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},
        {"http": "45.140.244.198:12323",  "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},
        {"http": "193.106.198.128:12323", "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},
        {"http": "136.175.224.210:12323", "auth": HTTPProxyAuth("14a2f5dfde475", "51ae51af21")},







    ]

    proxies0 = [
        {"http": "http://14a2f5dfde475:51ae51af21@185.124.241.179:12323"},
        {"http": "http://14a2f5dfde475:51ae51af21@89.116.242.185:12323"},
        {"http": "http://14a2f5dfde475:51ae51af21@45.140.244.198:12323"},
        {"http": "http://14a2f5dfde475:51ae51af21@193.106.198.128:12323"},
        {"http": "http://14a2f5dfde475:51ae51af21@136.175.224.210:12323"},
    ]

    # Rest of your code here..
    # with ThreadPoolExecutor(max_workers=20) as executor:
    #     futures = [executor.submit(vast_handling, url, headers=headers, params=params,call_num=i) for i in range(1,num_requests+1)]
    #     response = [future.result() for future in futures]
    #     print(response)

