from flask import Flask,render_template
from datetime import datetime
from flask_bcrypt import Bcrypt
from flask_mail import Mail,Message
from pymongo import MongoClient
from bson.objectid import ObjectId
import logging
from logging.handlers import RotatingFileHandler
from datetime import timedelta
from threading import Thread
import logging
from logging.handlers import RotatingFileHandler
import traceback
import requests
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

from Opcenter import config


smtp_server = 'email-smtp.us-east-2.amazonaws.com'
smtp_port = 587

# Sender and receiver email addresses
sender_email = 'no-reply@parablu.com'


# from jinja2 import Markup

app = Flask(__name__)

#Latest Files - 923



#Adding the logger
def setup_logger():
    logger = logging.getLogger('my_logger')
    logger.setLevel(logging.DEBUG)

    # create rotating file handler
    handler = RotatingFileHandler('Opcenter.log', maxBytes=1024*1024, backupCount=5)
    handler.setLevel(logging.DEBUG)

    # create formatter
    formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    handler.setFormatter(formatter)

    # add handler to logger
    logger.addHandler(handler)

    return logger


my_logger = setup_logger()


app.config['SECRET_KEY'] = config.SECRET_KEY

# app.config["MONGO_URI"] = "mongodb+srv://poorna:poorna@cluster0.u0uqpgf.mongodb.net/?retryWrites=true&w=majority"

# app.config["MONGO_URI"] = 'mongodb://neil:parablu@127.0.0.1:48765'

# app.config["MONGO_URI"] = config.DATABASE_URI
app.config["MONGO_URI"] = 'mongodb://neil:parablu@127.0.0.1:27017'


client = MongoClient(app.config["MONGO_URI"])

#Configuring session timeout
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(minutes=20)

# Define an error handler for 500 errors
@app.errorhandler(500)
def internal_server_error(e):
    tb = traceback.format_exc()
    my_logger.error(f"Exception occurred:\n{tb}")
    my_logger.info(e)
    # Redirect the user to the error page with a 500 status code
    return render_template('dashboard/errorpage.html', error_code=500), 500

# Define an error handler for 404 errors
@app.errorhandler(404)
def page_not_found(e):
    tb = traceback.format_exc()
    my_logger.error(f"Exception occurred:\n{tb}")
    my_logger.info(e)
    # Redirect the user to the error page with a 404 status code
    return render_template('dashboard/errorpage.html', error_code=404), 404

@app.errorhandler(Exception)
def handle_exception(e):
    # Render the error.html template with the error message
    tb = traceback.format_exc()
    my_logger.error(f"Exception occurred:\n{tb}")
    my_logger.info(e)
    my_logger.info("This is the error printinf from line 77")
    return render_template('dashboard/errorpage.html', error_code=str(e)), 500

@app.errorhandler(UnboundLocalError)
def handle_unbound_local_error(e):
    tb = traceback.format_exc()
    my_logger.error(f"Exception occurred:\n{tb}")
    my_logger.info(e)
    return render_template('dashboard/errorpage.html', error_code='UnboundLocalError: ' + "Internal Issue"), 500

@app.errorhandler(FileNotFoundError)
def handle_file_not_found_error(e):
    tb = traceback.format_exc()
    my_logger.error(f"Exception occurred:\n{tb}")
    my_logger.info(e)
    # Render the error.html template with the error message
    return render_template('dashboard/errorpage.html', error_code="File Not Found"), 404


# Add this function to disable caching
@app.after_request
def add_header(response):
    response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = '0'
    response.headers['Cache-Control'] = 'public, max-age=0'
    return response

# Route that raises an exception
@app.route('/error')
def error():
    raise Exception("Exception Test")


# define a custom filter function
def lstrip_zero(value):
    return str(value).lstrip('0')

def bytes_to_gb(bytes):
    totalMb = int(bytes)
    totalGb = totalMb/1024
    print("The total value of totalMB is")
    print(totalMb)
    if totalGb > 0:
        return str(round(totalGb,2)) + " GB"
    else:
        return bytes    

def format_date(date_str):
    date_obj = datetime.strptime(date_str, '%Y/%m/%d %H:%M:%S')
    formatted_date = datetime.strftime(date_obj, '%d %b %Y %H:%M')
    return formatted_date

def add_space(value):
    digits = ''.join(filter(str.isdigit, value))
    letter = ''.join(filter(str.isalpha, value))
    return f"{digits} {letter}"

def filesize_fmt(value):
    size_in_bytes = float(value.split()[0])
    value = float(size_in_bytes)
    units = ['B', 'KB', 'MB', 'GB']
    for unit in units:
        if value < 1024.0:
            return f"{value:.2f} {unit}"
        value /= 1024.0
    return f"{value:.2f} {units[-1]}"


def filesize_fmt1(value):
    value = float(value)
    units = ['MB', 'GB']
    for unit in units:
        if value < 1024.0:
            return f"{value:.2f} {unit}"
        value /= 1024.0
    return f"{value:.2f} {units[-1]}"



def format_date2(date_str):
    date_str = date_str.replace('IST', '+0530')
    date_obj = datetime.strptime(date_str, '%a %b %d %H:%M:%S %z %Y')
    formatted_date2 = datetime.strftime(date_obj, '%b %d %Y %H:%M')
    return formatted_date2
 

def add_comma(value):
    if isinstance(value, int) and value >= 1000:
        value_str = str(value)
        return '{},{}'.format(value_str[:-3], value_str[-3:])
    return value


def gb_to_tb(value):
    if isinstance(value, str) and value.endswith(' GB'):
        gb_value = float(value[:-3])
        if gb_value > 1024:
            tb_value = gb_value / 1024
            return '{:.2f} TB'.format(tb_value)
    return value

def kb_to_tb(value):
    if isinstance(value, str):
        kb_value = float(value[:-3])
        if kb_value > 1024:
            mb_value = kb_value / 1024
            if mb_value > 1024:
                gb_value = mb_value / 1024
                if gb_value > 1024:
                    tb_value = gb_value / 1024
                    return '{:.2f} TB'.format(tb_value)
                else:
                    return '{:.2f} GB'.format(gb_value)
            else:
                return '{:.2f} MB'.format(mb_value)
    return value

def merge_dicts(d1, d2):
    """
    Merges two dictionaries.
    """
    return {**d1, **d2}


# add the custom filter to the Jinja2 environment
@app.template_filter()
def capitalize_first_letter(value):
    if value:
        return value[0].upper() + value[1:]
    return ''


def truncate_words(text, num_words=10, ellipsis="..."):
    words = text.split()
    if len(words) <= num_words:
        return ' '.join(words)
    else:
        truncated_text = ' '.join(words[:num_words])
        return f'{truncated_text} {ellipsis}'


app.jinja_env.filters['merge_dicts'] = merge_dicts
app.jinja_env.filters['filesize_fmt1'] = filesize_fmt1
app.jinja_env.filters['filesize_fmt'] = filesize_fmt
app.jinja_env.filters['add_space'] = add_space
app.jinja_env.filters['format_date2'] = format_date2
app.jinja_env.filters['format_date'] = format_date
app.jinja_env.filters['lstrip_zero'] = lstrip_zero
app.jinja_env.filters['gb_to_tb'] = gb_to_tb
app.jinja_env.filters['kb_to_tb'] = kb_to_tb

app.jinja_env.filters['bytes_to_gb'] = bytes_to_gb
app.jinja_env.filters['add_comma'] = add_comma
app.jinja_env.filters['truncate_words'] = truncate_words


db = client.get_database('Opcenter')
dbs = client.get_database('MachinesDB')
session_dbssss = client.get_database('session_db')


#This for Authentication Users Information
auth = db.UsersData
#This for the Dashboard Data
usersTotal = dbs.usersTotal
colors = dbs.colors_set
CloudName = dbs.CloudName
LicenceData = dbs.LicenceData
AgentData = dbs.AgentVersion
blkuser = dbs.BlockUsers
LicenceDataAddil = dbs.LicenceDataAddi
spoc = dbs.spoc
MuxData = dbs.MuxData
MuxDatauserList = dbs.MuxDataUsers
sessions_collection = session_dbssss.sessions
deletedUs_ers = dbs.DeletedUsersList
soft_blk_usr = dbs.SoftBlockUsersList
dataprotectionList = dbs.dataprotection
loginHistory = db.loginHistory
bkplist = db.backupPolicyList
dashboardColor = dbs.dashboardColor
uptimeRobot = dbs.uptimeRobot
machineList = dbs.documentMachines
documentsMuxCurrent = dbs.documentsMuxCurrent
opcenterNotes = db.OpcenterNotes
oldEntries = db.oldEntries



class SessionManager:
    def __init__(self, mongo_uri):
        self.client = MongoClient(mongo_uri)
        self.db = self.client["session_db"]
        self.collection = self.db["sessions"]

    def add_session(self, user_id, session_id):
        self.collection.insert_one({"user_id": user_id, "session_id": session_id})

    def get_user_sessions(self, user_id):
        return list(self.collection.find({"user_id": user_id}))

    def logout_user(self, user_id):
        print("The user id is : ")
        print(user_id)
        self.collection.delete_one({"user_id": user_id})
        print("Successfully Deleted")

    def check_user_logged_in(self, user_id, session_id):
        sessions = self.get_user_sessions(user_id)
        for session in sessions:
            if session["session_id"] != session_id:
                self.destroy_session(session["user_id"], session["session_id"])
                return True
        return False
    
    def destroy_session(self, user_id,session_id):
        self.collection.delete_one({"user_id": user_id,"session_id":session_id})




session_manager = SessionManager(app.config["MONGO_URI"])


# Session timeout code
def check_session_timeout():
    timeout = timedelta(minutes=10)
    while True:
        # Get the last access time of the session
        last_access_time = session.get('_accessed', None)
        if last_access_time is not None and datetime.now() - last_access_time > timeout:
            # If the session has timed out, log the user out and remove the session data
            user = auth.find_one({"username": session['username']})
            print(user['_id'])
            SessionManager.destroy_session(user['_id'])
            session.pop('username', None)
            session.clear()
        # Wait for some time before checking the session timeout again
        time.sleep(60)

def start_session_timeout_thread():
    timeout_thread = Thread(target=check_session_timeout)
    timeout_thread.start()


print("Successfully inserted")

bcrypt = Bcrypt(app)

@app.route('/logs')
def show_logs():
    with open('Opcenter.log', 'r') as f:
        logs = f.read().splitlines()
    
    table = '<table>'
    for log in logs:
        table += f'<tr><td>{log[:19]}</td><td>{log[31:]}</td></tr>'
    table += '</table>'
    
    return table


def is_website_running(url):
    try:
        response = requests.get(url)
        return response.status_code == 200
    except requests.exceptions.RequestException as e:
        print(e)
        return False


def timestamp_to_human_readable(timestamp):
    dt_object = datetime.fromtimestamp(timestamp)
    return dt_object.strftime('%Y-%m-%d %H:%M:%S')

def get_domain_valid_until(domain_name):
    try:
        domain_info = whois.whois(domain_name)
        if isinstance(domain_info.expiration_date, list):
            expiration_date = domain_info.expiration_date[0]
        else:
            expiration_date = domain_info.expiration_date
        return expiration_date
    except Exception as e:
        print(f"Error: {e}")
        return None


    scheduler = BackgroundScheduler()
    scheduler.start()
    scheduler.add_job(ping_machine, 'interval', seconds=5)

def ping_machine():
    my_logger.info("This is the ping Machine Line")
    try:
        CloudNameList =  CloudName.distinct("System Name")
        my_logger.info(CloudNameList)
        my_logger.error("This is yhe cloiud name list")
        # urls = ["https://poornakashi.site","https://poorneakashi.site","https://poornsakashi.site","https://ibrelbk.parablu.com/BluKryptBuilder/healthcheck/","https://poornakashi.site","https://ceat.parablu.com","https://egbu.embassyindia.com","https://egbu.embassyindia.com/paracloud/healthcheck/","https://excelra.parablu.com/paracloud/healthcheck/"]
        
        for i in CloudNameList:
            url = "https://"+i
            print(url)
            result = is_website_running(url)

            current_timestamp = datetime.utcnow().timestamp()
            print(current_timestamp)
            print(result)
            offset_in_seconds = 5 * 3600 + 30 * 60
            ist_timestamp = current_timestamp + offset_in_seconds
            human_readable_time = datetime.fromtimestamp(ist_timestamp).strftime('%Y-%m-%d %H:%M:%S')
            print(human_readable_time)
            print("Get the Domain")

            if(result):
                uptimeDocument = {
                    "lastUpdateddatatime" : human_readable_time,
                    "lastUpdatedtimestamp" : current_timestamp,
                    "lastUpdatedist_timestamp" : ist_timestamp,
                    "fqdn" : i,
                    "pinggedUrl" : url,
                    "result":result,
                    "status":"successfull",
                    "mailTriggered" : "No",
                } 
            else:
                uptimeDocument = {
                    "lastUpdateddatatime" : human_readable_time,
                    "lastUpdatedtimestamp" : current_timestamp,
                    "lastUpdatedist_timestamp" : ist_timestamp,
                    "fqdn" : i,
                    "pinggedUrl" : url,
                    "result":result,
                    "status":"unsuccessfull",
                    "mailTriggered" : "Yes"
                } 

                try:
                    # triggerMail(i)
                    print("Mail triggered successfully")
                except:
                    print("Mail triggered unsuccessfully")

            uptimeRobot.insert_one(uptimeDocument)

            print("The document inserted successfully")

        print("Hello World this is ping Machine")
        my_logger.info("This is the ping machine")
    except Exception as e:
        print("Error while pinging the machine:", e)



def triggerMail(machineName):
    message = MIMEMultipart()
    message['From'] = "no-reply@parablu.com"
    message['To'] = "poornakashi@gmail.com"
    message['Subject'] = "Test Email"
            # Modify the email body string to include multiple lines
    body = "Buddy Machine was"+machineName+" down \n\n"
    body += "Kindly save the following information:\n"
    body += "This information is confidential and should not be shared with anyone."
    message.attach(MIMEText(body, 'plain'))
    
    body += "Visit https://www.opcenterv2.parablu.com"
    # Create a SMTP session
    smtp_session = smtplib.SMTP(smtp_server, smtp_port)
    smtp_session.starttls() # Secure the connection

    # Login with credentials
    smtp_session.login('AKIAVZLS53X5PBNLV5GJ', 'BLPn8AcrtZTJz+BdyyTCccNQqj6UxIB/kvKMv9AWd/mB')

    # Send the message via the SMTP server
    smtp_session.sendmail(sender_email, email, message.as_string())

    # Terminate the SMTP session
    smtp_session.quit()
    print("The Mail Sent Successfully!!!")
    



# from apps.authentication.routes import auth_blueprint
from Opcenter.apps.authentication.routes import auth_blueprint
from Opcenter.apps.main.routes import main
from Opcenter.apps.admin.routes import admin

app.register_blueprint(auth_blueprint)
app.register_blueprint(main)
app.register_blueprint(admin)
