import random
import string
import logging
import os
from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.http import MediaFileUpload
from googleapiclient.errors import HttpError
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from werkzeug.utils import secure_filename
import pycountry
import phonenumbers
from phonenumbers import geocoder
from phonenumbers.phonenumberutil import region_code_for_country_code
from setup import headers_tookan, PLANDAY_CLIENT_ID, PLANDAY_REFRESH_TOKEN, ALLOWED_EXTENSIONS
from models import get_cred_google_service_acc, get_data_from_fk_table
from datetime import datetime, timedelta
import requests
import json
import ast
import mimetypes
import re

logging.basicConfig(level=logging.INFO)

from telegram import __version__ as TG_VER
try:
    from telegram import __version_info__
except ImportError:
    __version_info__ = (0, 0, 0, 0, 0)  # type: ignore[assignment]

import telegram
from telegram.ext import Application, CommandHandler, ContextTypes, MessageHandler, filters

async def send_msg_telegram(TOKEN, msg, id):
    """
    Senden Nachrichten an Telegramuser/Grupper

    Args:
        TOKEN String: Token für Telegram Bot
        msg String: Nachricht 
        id int: id für den Chat
    """
        
        
    bot = telegram.Bot(TOKEN)
    await bot.send_message(id, msg, parse_mode='HTML')

SCOPES = ['https://www.googleapis.com/auth/drive']


def parse_file_data(data_list):
    """
    Converts a list containing string representations of dictionaries into a list of actual dictionaries.
    
    Args:
    - data_list (list): List containing string representations of dictionaries.
    
    Returns:
    - list[dict]: A list of dictionaries parsed from the input list of strings.
    """
    converted_list = []
    for item in data_list:
        try:
            # Parse each string item as a dictionary
            parsed_item = ast.literal_eval(item)
            # Add the parsed dictionary to the new list
            converted_list.append(parsed_item)
        except (ValueError, SyntaxError) as e:
            print(f"Error parsing item: {item}\nError: {e}")
            continue
    return converted_list

def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


def compare_changes(old_data: dict, new_data: dict) -> list:
    """
    Vergleicht alte und neue Daten und gibt eine Liste von Änderungen zurück.

    Args:
        old_data (dict): Alte Daten aus der Datenbank.
        new_data (dict): Neue Daten aus dem Formular.

    Returns:
        list: Liste von Änderungen, z.B.
        [{"field": "email", "old": "old@example.com", "new": "new@example.com"}, ...]
    """
    changes = []
    for key in new_data:
        old_val = old_data.get(key)
        new_val = new_data.get(key)
        if str(old_val) != str(new_val):  # cast to string for safe comparison
            changes.append({
                "field": key,
                "old": old_val,
                "new": new_val
            })
    return changes


def find_folder_id(service, folder_name, parent_id):
    """
    Find the Google Drive folder ID based on folder name within a specified parent folder.

    Args:
        service: Authenticated Google Drive service instance.
        folder_name (str): Name of the folder.
        parent_id (str): ID of the parent folder.

    Returns:
        str: Folder ID if found, None otherwise.
    """
    query = f"mimeType='application/vnd.google-apps.folder' and name='{folder_name}' and '{parent_id}' in parents"
    response = service.files().list(q=query, spaces='drive', fields='files(id, name)' ,includeItemsFromAllDrives= True,supportsAllDrives=True).execute()
    folders = response.get('files', [])

    if not folders:
        return None
    # If multiple folders have the same name, this returns the ID of the first one.
    return folders[0]['id']

def check_folder_exists(service, folder_id):
    try:
        # Attempt to get the folder metadata to check if it exists
        folder = service.files().get(fileId=folder_id, fields='id',supportsAllDrives=True).execute()
        return True
    except HttpError as error:
        logging.error("An error occurred: %s", error)
        if error.resp.status == 404:
            logging.error("Folder with ID %s not found.", folder_id)
        return False

def upload_files_to_google_drive(file_paths, folder_name, parent_id='1ihoY1RD5HNY1itj_lkcdPhp3aWM8j6kt'):
    """
    Uploads multiple files to Google Drive to a specified folder by folder name, within a specified parent folder.
    Automatically determines the MIME type of each file based on its extension.

    Args:
        file_paths (list of str): List of file paths.
        folder_name (str): The name of the Google Drive folder where the files will be uploaded.
        parent_id (str): ID of the parent folder where the target folder resides.

    Returns:
        list: A list of URLs or error messages for each file uploaded.
    """
    # Load credentials from a service account file
    cred_json = get_cred_google_service_acc()  # Ensure this function correctly fetches your credentials
    try:
        credentials = service_account.Credentials.from_service_account_info(
            cred_json, scopes=SCOPES)
    except Exception as e:
        logging.error("Failed to load service account credentials: %s", e)
        return ["Error loading service account credentials."]

    # Build the Google Drive service object
    try:
        service = build('drive', 'v3', credentials=credentials)
    except Exception as e:
        logging.error("Failed to build the Drive service: %s", e)
        return ["Error building the Drive service."]

    # Resolve folder name to ID within the specified parent
    folder_id = find_folder_id(service, folder_name, parent_id)
    if not folder_id:
        return ["Folder not found or inaccessible."]

    results = []

    # Iterate over each file path
    for file_path in file_paths:
        file_name = file_path.split('/')[-1]  # Extract the file name from the path
        mime_type, _ = mimetypes.guess_type(file_path)  # Guess MIME type
        if not mime_type:
            mime_type = 'application/octet-stream'  # Default MIME type if unknown

        # File metadata for the upload
        file_metadata = {
            'name': file_name,
            'parents': [folder_id]
        }

        # Media file upload
        print(file_path)
        media = MediaFileUpload(file_path, mimetype=mime_type)

        # Attempt to upload the file and fetch its URL
        try:
            file = service.files().create(body=file_metadata, media_body=media, fields='id, webViewLink', supportsAllDrives=True).execute()
            results.append({'name': file_name, 'url':file['webViewLink'], 'id':file['id'], 'operation': 'insert'})
        except Exception as error:
            error_message = f"Error uploading the file {file_name}: {error}"
            logging.error(error_message)
            results.append(error_message)

    return results


def list_files_in_folder(folder_name, parent_id='1ihoY1RD5HNY1itj_lkcdPhp3aWM8j6kt'):
    """
    List all files in a specified Google Drive folder using the folder name.

    Args:
        service: Authenticated Google Drive service instance.
        folder_name (str): The name of the folder.
        parent_id (str): The ID of the parent folder (default is 'root' for the main directory).

    Returns:
        list: A list of dictionaries, each containing details about the files within the folder.
    """
    cred_json = get_cred_google_service_acc()
    try:
        credentials = service_account.Credentials.from_service_account_info(
            cred_json, scopes=SCOPES)
    except Exception as e:
        logging.error("Failed to load service account credentials: %s", e)
        return "Error loading service account credentials."

    # Build the Google Drive service object
    try:
        service = build('drive', 'v3', credentials=credentials)
    except Exception as e:
        logging.error("Failed to build the Drive service: %s", e)
        return "Error building the Drive service."
    
    folder_id = find_folder_id(service, folder_name, parent_id)
    if folder_id is None:
        logging.error("Folder not found or inaccessible")
        return []

    try:
        query = f"'{folder_id}' in parents"
        results = service.files().list(q=query, spaces='drive', fields='files(id, name, mimeType, size, webViewLink, createdTime, modifiedTime)', supportsAllDrives=True, includeItemsFromAllDrives=True).execute()
        files = results.get('files', [])
        filtered_files = [file for file in files if "DELETED" not in file['name'].upper()]
        return filtered_files
    except Exception as e:
        logging.error("Failed to retrieve files: %s", e)
        return []
    

def rename_file(file_id, name):
    cred_json = get_cred_google_service_acc()
    try:
        credentials = service_account.Credentials.from_service_account_info(
            cred_json, scopes=SCOPES)
    except Exception as e:
        logging.error("Failed to load service account credentials: %s", e)
        return "Error loading service account credentials."

    # Build the Google Drive service object
    try:
        service = build('drive', 'v3', credentials=credentials)
    except Exception as e:
        logging.error("Failed to build the Drive service: %s", e)
        return "Error building the Drive service."
    
    file_metadata = {'name': name}
    try:
        updated_file = service.files().update(fileId=file_id, body=file_metadata, supportsAllDrives=True).execute()
        return updated_file
    except Exception as e:
        logging.error("Failed to rename file: %s", e)
        return None
    
def get_country_dialing_codes():
    country_dial_codes = []
    country_names = []
    seen_dial_codes = set()  # Set to track seen dial codes

    # Iterate over all countries provided by pycountry
    for country in pycountry.countries:
        try:
            country_code = country.alpha_2
            example_number = phonenumbers.example_number(country_code)
            if example_number:
                dial_code = '+' + str(example_number.country_code)
                if dial_code not in seen_dial_codes:  # Check if the dial code has been seen
                    seen_dial_codes.add(dial_code)
                    country_dial_codes.append({
                        'dial_code': dial_code,
                        'region_code': phonenumbers.region_code_for_number(example_number)
                    })
                    country_names.append({
                        'country': country.name
                    })
        except Exception as e:
            print(f"Error processing country: {country.name}, {e}")
             
    kosovo_dial_code = '+383'
    if kosovo_dial_code not in seen_dial_codes:
        country_dial_codes.append({
            'dial_code': kosovo_dial_code,
            'region_code': 'XK'
        })
        country_names.append({
            'country': 'Kosovo'
        })


    country_names.append({
        'country': 'Morocco'
    })

    # Sorting the dial codes numerically (removing '+' and converting to int for sorting)
    country_dial_codes.sort(key=lambda x: int(x['dial_code'][1:]))

    country_names.sort(key=lambda x: x['country'])

    return country_dial_codes, country_names

def create_google_folder(folder_name, parent_id="1ihoY1RD5HNY1itj_lkcdPhp3aWM8j6kt"):
    """
    Creates a folder in Google Drive with the given name, within a specified parent folder.

    Args:
        folder_name (str): The name of the folder to create.
        parent_id (str): The ID of the parent folder in which to create the new folder (default is set).

    Returns:
        str: The URL of the created folder, or a message indicating an error.
    """
    # Create credentials using the service account file
    cred_json = get_cred_google_service_acc()

    try:
        credentials = service_account.Credentials.from_service_account_info(
            cred_json, scopes=SCOPES)
    except Exception as e:
        logging.error("Failed to load service account credentials: %s", e)
        return "Error loading service account credentials."

    # Build the Google Drive service object
    try:
        service = build('drive', 'v3', credentials=credentials)
    except Exception as e:
        logging.error("Failed to build the Drive service: %s", e)
        return "Error building the Drive service."

    # Check if the parent folder exists and is accessible
    if not check_folder_exists(service, parent_id):
        return f"Parent folder with ID {parent_id} not found or inaccessible."

    # File metadata for folder creation
    file_metadata = {
        'name': folder_name,
        'mimeType': 'application/vnd.google-apps.folder',
        'parents': [parent_id] if parent_id else []
    }
    
    # Attempt to create the folder and fetch its URL
    try:
        file = service.files().create(body=file_metadata, fields='id, webViewLink', supportsAllDrives=True).execute()
        folder_url = file.get('webViewLink')
        return folder_url
    except HttpError as error:
        logging.error("An error occurred during folder creation: %s", error)
        return "Error creating the folder."
    
def get_company_new_data_from_form(request):
    # Company fields
    industry_map = {str(item['id']): item['description'] for item in get_data_from_fk_table('company_industry', 1)}
    status_map = {str(item['id']): item['description'] for item in get_data_from_fk_table('company_status', 1)}
    company_fields = {
        key: (None if val in ['None', ''] else val)
        for key, val in request.form.items()
        if key not in ('row_id', 'id', 'submitAction')
        and not key.startswith('contact_')
        and not key.startswith('files')
    }
    if 'industry' in company_fields and company_fields['industry'] in industry_map:
        company_fields['industry'] = industry_map[company_fields['industry']]

    if 'status' in company_fields and company_fields['status'] in status_map:
        company_fields['status'] = status_map[company_fields['status']]
    # Contacts
    contacts = {}
    i = 1
    while request.form.get(f'contact_name_{i}'):
        contact_id = request.form.get(f'contact_id_{i}') or f"new_{i}"
        contacts[str(contact_id)] = {
            "name": request.form.get(f'contact_name_{i}'),
            "email": request.form.get(f'contact_email_{i}'),
            "phone": request.form.get(f'contact_phone_{i}'),
            "prefix": request.form.get(f'contact_prefix_{i}'),
            "job_title": request.form.get(f'contact_job_title_{i}')
        }
        i += 1

    return {
        "company": company_fields,
        "contacts": contacts
    }


def get_customer_interactions_new_data_from_form(request):
    # Company fields
    company_map = {str(item['id']): item['description'] for item in get_data_from_fk_table('company', 1)}
    communication_over_map = {str(item['id']): item['description'] for item in get_data_from_fk_table('communication_method', 1)}
    company_contact_person_map = {str(item['id']): item['description'] for item in get_data_from_fk_table('company_contact_person', 2)}
    customer_interactions_fields = {
        key: (None if val in ['None', ''] else val)
        for key, val in request.form.items()
        if key not in ('row_id', 'id', 'submitAction')
        and not key.startswith('contact_')
        and not key.startswith('files')
    }
    if 'company' in customer_interactions_fields and customer_interactions_fields['company'] in company_map:
        customer_interactions_fields['company'] = company_map[customer_interactions_fields['company']]

    if 'communicated_over' in customer_interactions_fields and customer_interactions_fields['communicated_over'] in communication_over_map:
        customer_interactions_fields['communicated_over'] = communication_over_map[customer_interactions_fields['communicated_over']]

    if 'company_user' in customer_interactions_fields and customer_interactions_fields['company_user'] in company_contact_person_map:
        customer_interactions_fields['company_user'] = company_contact_person_map[customer_interactions_fields['company_user']]

    return {
        "customer_interactions": customer_interactions_fields
    }


def get_change_diff_customer_interaction(old_data, new_data):
    changes = {
        "customer_interactions": {},
    }

    # Compare company fields
    for key, new_val in new_data['customer_interactions'].items():
        old_val = old_data['customer_interactions'].get(key)
        if str(old_val) != str(new_val):
            changes["customer_interactions"][key] = {"old": old_val, "new": new_val}


    return changes

def get_change_diff(old_data, new_data):
    changes = {
        "company": {},
        "contacts": {}
    }

    # Compare company fields
    for key, new_val in new_data['company'].items():
        old_val = old_data['company'].get(key)
        if str(old_val) != str(new_val):
            changes["company"][key] = {"old": old_val, "new": new_val}

    # Compare contact persons
    for cid, new_contact in new_data['contacts'].items():
        old_contact = old_data['contacts'].get(cid, {})
        for key, new_val in new_contact.items():
            old_val = old_contact.get(key)
            if str(old_val) != str(new_val):
                if cid not in changes["contacts"]:
                    changes["contacts"][cid] = {}
                changes["contacts"][cid][key] = {"old": old_val, "new": new_val}

    return changes


def parse_int(value):
    """Konvertiert value in einen Integer, falls möglich, sonst None."""
    if not value or str(value).lower() == 'none':
        return None
    try:
        return int(value)
    except ValueError:
        return None