import os
import sys
from typing import Optional

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../")))
import json
from utilities.hash_utilities import create_hash
import datetime as dt
from models.userModels import (
    UserCredentials,
    AuthenticatedUser,
    UserRoles,
    CreateUserCredentials,
    GoogleLoginResult,
)
from google.oauth2 import id_token
from google.auth.transport import requests as grequests
from models.userModels import UserOperations, CreateUserGoogleCredentials
from models.session_models import SessionOperations
from flask import g, request
from models.leads_models import CreateLeadInput, LeadOperations
from utilities.logging_functions import echo_log
from models.ticket_models import CreateTicketInput, TicketOperations
from models.note_models import CreateNoteInput, NoteOperations

TEST_DB_PATH = os.path.join(os.path.dirname(__file__), "..", "test_data.json")

log_file = "graphql.resolvers.mutation_resolvers.py"


def create_account_resolver(
    obj, info, createUserCredentials: CreateUserCredentials
) -> str:

    userName = createUserCredentials.get("userName")
    password = createUserCredentials.get("password")
    role = createUserCredentials.get(
        "role", UserRoles.USER
    )  # Default to USER if not provided
    # Load existing data
    with open(TEST_DB_PATH, "r", encoding="utf-8") as f:
        data = json.load(f)

    # Check if user already exists
    if any(u["userName"] == userName for u in data.get("userAuth", [])):
        return "User already exists"

    # Hash the password
    password_hash = create_hash(
        value_1=userName,
        value_2=password,
        hash_algorithm="blake2b",
        salt="fireflysolar",
    )

    # Create new user record
    new_id = max([u["id"] for u in data.get("userAuth", [])], default=0) + 1
    new_user = {
        "id": new_id,
        "userName": userName,
        "passwordHash": password_hash,
        "role": role,
        "lastLogin": None,
        "isActive": True,
        "failedLoginAttempts": 0,
        "createdAt": dt.datetime.now().isoformat(),  # Store creation time in ISO format
    }

    data.setdefault("userAuth", []).append(new_user)

    # Save back to file
    with open(TEST_DB_PATH, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2)

    # Return the new user (excluding passwordHash for security)
    user_copy = new_user.copy()
    user_copy.pop("passwordHash")
    return "User created successfully: " + json.dumps(user_copy, indent=2)


def login_resolver(
    obj, info, userCredentials: UserCredentials
) -> AuthenticatedUser | str:

    userName = userCredentials.get("userName")
    password = userCredentials.get("passwordHash")
    # Load existing data
    with open(TEST_DB_PATH, "r", encoding="utf-8") as f:
        data = json.load(f)

    # Find user by userName
    user = next(
        (u for u in data.get("userAuth", []) if u["userName"] == userName), None
    )
    if not user or not user.get("isActive", True):
        print("Invalid username or account inactive")
        return {"user": None, "error": "Invalid username or account inactive"}

    # Hash the provided password using the same method as account creation
    password_hash = create_hash(
        value_1=userName,
        value_2=password,
        hash_algorithm="blake2b",
        salt="fireflysolar",
    )

    # Compare hashes
    if password_hash != user["passwordHash"]:
        # Increment failedLoginAttempts
        user["failedLoginAttempts"] = user.get("failedLoginAttempts", 0) + 1
        # Lock account if failed 5 times
        if user["failedLoginAttempts"] >= 5:
            user["isActive"] = False
            print(
                f"User {userName} has been locked due to too many failed login attempts."
            )
        with open(TEST_DB_PATH, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2)
        print("Invalid password")
        return {"user": None, "error": "Invalid password"}

    print(f"[INFO] User {userName} logged in successfully.")
    # Reset failedLoginAttempts and update lastLogin
    user["failedLoginAttempts"] = 0
    user["lastLogin"] = (
        dt.datetime.now().isoformat()
    )  # Store last login time in ISO format

    # Save updated data
    with open(TEST_DB_PATH, "w", encoding="utf-8") as f:
        json.dump(data, f, indent=2)

    # Return user info (excluding passwordHash)

    authenticatedUser: AuthenticatedUser = {
        "user": {
            "id": user["id"],
            "userName": user["userName"],
            "role": UserRoles(user["role"]).value,
            "lastLogin": user.get("lastLogin"),
            "isActive": user.get("isActive", True),
            "createdAt": user.get("createdAt"),
        },
        "error": None,
    }
    return authenticatedUser


def google_login_resolver(obj, info, token: str) -> GoogleLoginResult:
    # For testing purposes, we'll just decode the token and extract the email
    # In a real implementation, you would verify the token with Google's API
    GOOGLE_CLIENT_ID = (
        "1663246028-8d7hmgmqn37uungj2neaaef3g1fpc18c.apps.googleusercontent.com"
    )
    ALLOWED_DOMAIN = "fireflysolar.ca"
    # Simulated decoded token (in reality, decode and verify the JWT)
    id_info = id_token.verify_oauth2_token(token, grequests.Request(), GOOGLE_CLIENT_ID)

    email = id_info.get("email")
    email_verified = id_info.get("email_verified")
    if not email_verified or not email.endswith(f"@{ALLOWED_DOMAIN}"):
        raise Exception("Unauthorized")

    db_client = g.db_client
    db_client._ensure_connection()
    user_operations = UserOperations(db_client=db_client)

    # Check if user exists
    existing_user = user_operations.check_user_exists(email)

    name = id_info.get("name")
    sub = id_info.get("sub")

    if not existing_user:
        # Create new user
        create_user_data: CreateUserGoogleCredentials = {
            "role_id": 2,  # Assuming '2' corresponds to a standard user role
            "name": name,
            "email": email,
            "google_sub": sub,
            "status": "active",
        }
        creation_message = user_operations.create_user_google(create_user_data)
        echo_log(creation_message, log_file)

    else:
        # Login existing user
        echo_log(f"User with email {email} exists. Proceeding to login.", log_file)
        login_result = user_operations.login_user_google(email=email, google_sub=sub)

        echo_log(f"Login result: {login_result}", log_file)

    user = user_operations.get_user_by_email(email=email)
    role_id = user.get("role_id")
    role = user_operations.get_role_by_id(role_id=role_id) if role_id else None

    # ----- SESSION CREATION -----
    session_ops = SessionOperations(db_client=db_client)
    user_agent = request.headers.get("User-Agent")
    ip_address = request.remote_addr

    session_id, expires_at = session_ops.create_session(
        session_data={
            "user_id": user.get("id"),
            "user_agent": user_agent,
            "ip_address": ip_address,
        }
    )

    # Stash this so the Flask route can set the cookie
    g.new_session_id = session_id

    # You can keep accessToken for now, or eventually drop it
    return {
        "accessToken": "session",  # not really needed once you rely on cookie
        "user": {
            "email": email,
            "name": name,
            "id": user.get("id"),
            "role": role,
            "dept_id": user.get("dept_id"),
        },
    }


def logout_resolver(obj, info) -> bool:
    # If there is a current session, revoke it
    if getattr(g, "current_session", None) is not None:
        session_id = g.current_session["session_id"]
        db_client = g.db_client
        sessions = SessionOperations(db_client)
        sessions.revoke_session(session_id)
    # It's fine to always return true
    return True


def create_lead_resolver(obj, info, leadData: CreateLeadInput) -> Optional[int]:

    try:
        db_client = g.db_client
        db_client._ensure_connection()
        lead_operations = LeadOperations(db_client=db_client)

        lead_id = lead_operations.create_lead(lead=leadData)
        echo_log(f"Lead created with ID: {lead_id}", log_file=log_file)
        if lead_id:
            return lead_id
        else:
            echo_log("Failed to create lead.", log_file=log_file)
            return None

    except Exception as e:
        echo_log(
            f"Error in create_lead_resolver with input {leadData}: {e}",
            log_file=log_file,
        )
        return None


def create_ticket_resolver(obj, info, ticketData: CreateTicketInput) -> Optional[int]:

    try:
        db_client = g.db_client
        db_client._ensure_connection()
        ticket_operations = TicketOperations(db_client=db_client)

        ticket_id = ticket_operations.create_ticket(ticket=ticketData)
        echo_log(f"Ticket created with ID: {ticket_id}", log_file=log_file)
        if ticket_id:
            return ticket_id
        else:
            echo_log("Failed to create ticket.", log_file=log_file)
            return None

    except Exception as e:
        echo_log(
            f"Error in create_ticket_resolver with input {ticketData}: {e}",
            log_file=log_file,
        )
        return None


def create_note_resolver(obj, info, noteData: CreateNoteInput) -> Optional[int]:

    try:
        db_client = g.db_client
        db_client._ensure_connection()
        note_operations = NoteOperations(db_client=db_client)

        note_id = note_operations.create_note(note=noteData)
        echo_log(f"Note created with ID: {note_id}", log_file=log_file)
        if note_id:
            return note_id
        else:
            echo_log("Failed to create note.", log_file=log_file)
            return None

    except Exception as e:
        echo_log(
            f"Error in create_note_resolver with input {noteData}: {e}",
            log_file=log_file,
        )
        return None
