import logging
import os
import random
import sqlite3
import string
from datetime import datetime, timedelta
from functools import wraps

import requests
from authlib.integrations.flask_client import OAuth
from dotenv import load_dotenv
from flask import (
    Flask,
    g,
    jsonify,
    make_response,
    redirect,
    render_template,
    request,
    session,
    url_for,
)
from flask_wtf.csrf import CSRFError, CSRFProtect

from config import DevelopmentConfig, ProductionConfig
from flask_session import Session

# Import the init_db function from init_db.py
from init_db import init_db

# Load environment variables
load_dotenv()

# Initialize Flask app
app = Flask(__name__)
csrf = CSRFProtect(app)

# Choose config based on environment
if os.getenv('MODE') == 'production':
    app.config.from_object(ProductionConfig)
else:
    app.config.from_object(DevelopmentConfig)

# Initialize session
Session(app)

# Initialize the database at startup (once)
with app.app_context():
    db_name = init_db()

# Set up logging
logging.basicConfig(level=logging.INFO)

# OAuth configuration for Google login
oauth = OAuth(app)
google = oauth.register(
    name='google',
    client_id=app.config['GOOGLE_ID'],
    client_secret=app.config['GOOGLE_SECRET'],
    server_metadata_url='https://accounts.google.com/.well-known/openid-configuration',
    client_kwargs={
        'scope': 'openid email profile',
        'access_type': 'offline'  # This requests the refresh token
        }
)

# Database helper function
def get_db():
    conn = sqlite3.connect(db_name)
    # Easier to work with dict-like rows
    conn.row_factory = sqlite3.Row
    return conn

def login_required(f):
    @wraps(f)
    def decorated_function(*args, **kwargs):
        google_token = session.get("google_token", None)
        if google_token:
            current_timestamp = datetime.now().timestamp()
            expires_at = int(google_token["expires_at"])
            expires_at_time = datetime.fromtimestamp(google_token["expires_at"])
            if current_timestamp >= expires_at:
                revoke_token(google_token)
                session.clear()
                return redirect(url_for('unauthorized'))
            else:
                app.logger.info(f"Token Expires at: {expires_at_time}")
        else:
            return redirect(url_for('unauthorized'))
        return f(*args, **kwargs)
    return decorated_function


def revoke_token(google_token):
    if google_token:
        token = google_token['access_token']
        # Send a request to Google's token revocation endpoint
        revoke_url = 'https://oauth2.googleapis.com/revoke'
        params = {'token': token}
        response = requests.post(revoke_url, params=params)
        
        if response.status_code == 200:
            app.logger.info("Successfully logged out from Google.")
        else:
            app.logger.error(f"Failed to revoke Google token. Response: {response.content}")
    

# Global variable to hold the current year
@app.before_request
def inject_current_year():
    g.current_year = datetime.now().year  # Add current_year to the template context

# CSRF Error Handler (built-in CSRF handling in Flask-WTF will automatically take care of most cases)
@app.errorhandler(CSRFError)
def handle_csrf_error(error):
    return jsonify({'status': 'error', 'message': 'CSRF token is missing or invalid'}), 400

@app.route('/unauthorized')
def unauthorized():
    return render_template('403.html'), 403

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

@app.route('/')
def home():
    return render_template('home.html', current_year=g.current_year)

@app.route('/login')
def login():
    # Generate a random state value
    state = ''.join(random.choices(string.ascii_letters + string.digits, k=32))
    
    # Store the state in the session
    session['oauth_state'] = state
    
    # Create the redirect URI with the state parameter
    redirect_uri = url_for('authorized', _external=True, _scheme=request.scheme)
    
    app.logger.info(f"Redirect URI: {redirect_uri}")
    
    # Redirect to Google's OAuth page with the state
    return google.authorize_redirect(redirect_uri, state=state, )

    
@app.route('/login/authorized')
def authorized():
    state = request.args.get('state')
    if state != session.get('oauth_state'):
        app.logger.error(f"State mismatch! Expected {session.get('oauth_state')}, but got {state}")
        return redirect(url_for('login'))  # Redirect to login if state is invalid
    
    
    # Fetch the token and user info from Google
    token = google.authorize_access_token()
    
    access_token = token.get('access_token', '')
    user_info = google.get('https://www.googleapis.com/oauth2/v1/userinfo').json()

    # Clear previous session information if a new login occurs
    session.clear()

    # Store the token and user info in the session
    session['google_token'] = token
    session['user'] = user_info

    # Register user in the database if it's their first login
    conn = get_db()
    cursor = conn.cursor()

    # Check if the user already exists in the users table
    cursor.execute("SELECT * FROM users WHERE email = ?", (user_info['email'],))
    existing_user = cursor.fetchone()

    if not existing_user:
        # User doesn't exist, insert a new record
        # Extract the access token string from the token
        access_token = token.get('access_token', '')

        cursor.execute("""
            INSERT INTO users (google_id, email, name, first_name,
            verified_email, picture_url, access_token, created_time, last_login_time)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (user_info['id'], user_info['email'], user_info['name'], user_info['given_name'],
              user_info['verified_email'], user_info['picture'], access_token,
              datetime.now().strftime('%Y-%m-%d %H:%M:%S'),  # created_time
              datetime.now().strftime('%Y-%m-%d %H:%M:%S')))  # last_login_time
        conn.commit()
        
    else:
        # User exists, update last_login_time and increment login_count
        cursor.execute("""
            UPDATE users
            SET last_login_time = ?, login_count = login_count + 1, access_token = ?
            WHERE email = ?
        """, (datetime.now().strftime('%Y-%m-%d %H:%M:%S'), access_token, user_info['email']))
        conn.commit()

    conn.close()

    return redirect(url_for('dashboard'))


@app.route('/profile')
@login_required
def profile():
    user_info = session.get('user')
    return render_template('profile.html', user_info=user_info)

@app.route('/update_profile', methods=['POST'])
@login_required
def update_profile():
    data = request.get_json()
    user_info = session['user']

    for key, value in data.items():
        if key in user_info:
            user_info[key] = value

    session['user'] = user_info
    return jsonify({'status': 'success', 'message': 'Profile updated successfully'})

@app.route('/dashboard')
@login_required
def dashboard():
    return render_template('dashboard.html')

@app.route('/tasks')
@login_required
def tasks():
    user_email = session.get("user")["email"]

    conn = get_db()
    cursor = conn.cursor()
    cursor.execute("""
        SELECT id, task_name, priority, assignee, description,
               task_start_date, task_end_date, updated_date, updated_count
        FROM tasks WHERE email = ?
    """, (user_email,))
    records = cursor.fetchall()
    conn.close()

    return render_template('tasks.html', tasks=records)

@app.route('/add_task', methods=['POST'])
@login_required
def add_task():
    user_email = session.get("user")["email"]
    task_data = {
        'task_name': request.form['task_name'],
        'priority': request.form['priority'],
        'assignee': request.form['assignee'],
        'description': request.form['description'],
        'task_start_date': request.form['task_start_date'],
        'task_end_date': request.form['task_end_date'],
        'updated_count': 0
    }

    conn = get_db()
    cursor = conn.cursor()
    cursor.execute('''
        INSERT INTO tasks (email, task_name, priority, assignee, description, task_start_date, task_end_date)
        VALUES (?, ?, ?, ?, ?, ?, ?)
    ''', (user_email, task_data['task_name'], task_data['priority'], task_data['assignee'],
          task_data['description'], task_data['task_start_date'], task_data['task_end_date']))
    conn.commit()
    task_data['id'] = cursor.lastrowid
    conn.close()

    return jsonify({'status': 'success', 'task': task_data})

@app.route('/update_task', methods=['POST'])
@login_required
def update_task():
    task_data = {
        'id': request.form['task_id'],
        'task_name': request.form['task_name'],
        'priority': request.form['priority'],
        'assignee': request.form['assignee'],
        'description': request.form['description'],
        'task_start_date': request.form['task_start_date'],
        'task_end_date': request.form['task_end_date'],
        'updated_count': int(request.form['updated_count']) + 1,
        'updated_date': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
    }

    conn = get_db()
    cursor = conn.cursor()
    cursor.execute('''
        UPDATE tasks
        SET task_name = ?, priority = ?, assignee = ?, description = ?, 
            task_start_date = ?, task_end_date = ?, updated_date = ?, updated_count = ?
        WHERE id = ?
    ''', (task_data['task_name'], task_data['priority'], task_data['assignee'], task_data['description'],
          task_data['task_start_date'], task_data['task_end_date'], task_data['updated_date'], 
          task_data['updated_count'], task_data['id']))
    conn.commit()
    conn.close()

    return jsonify({'status': 'success', 'task': task_data})

@app.route('/delete_task', methods=['POST'])
@login_required
def delete_task():
    task_id = request.form['task_id']

    conn = get_db()
    cursor = conn.cursor()
    cursor.execute('DELETE FROM tasks WHERE id = ?', (task_id,))
    conn.commit()
    conn.close()

    return jsonify({'status': 'success', 'task_id': task_id})

@app.route('/settings')
@login_required
def settings():
    return render_template('settings.html')

@app.route('/about')
def about():
    return render_template('about.html')

@app.route('/contact')
def contact():
    return render_template('contact.html')

@app.route('/send-message', methods=['POST'])
def send_message():
    name = request.form.get('name')
    email = request.form.get('email')
    subject = request.form.get('subject')
    message = request.form.get('message')
    
    return jsonify({'status': 'success', 'message': "Message sent successfully."})


@app.route('/logout')
@login_required
def logout():
    # Revoke the OAuth token with Google
    try:
        # Get the Google token from the session
        google_token = session.get('google_token')
        
        revoke_token(google_token)

        # Clear the Flask session
        session.clear()

    except Exception as e:
        app.logger.error(f"Error during logout: {e}")

    # Redirect to home page after logout
    return redirect(url_for('home'))


if __name__ == '__main__':
    app.run(debug=True, port=5000)
