"""
NEUROBRO SCALPING SYSTEM V6.0 - WEB DASHBOARD
Dark Trading Terminal UI with Real-time Updates
"""

from flask import Flask, render_template, request, jsonify, Response
import yfinance as yf
import pandas as pd
import numpy as np
import warnings
import json
from datetime import datetime, timedelta
import threading
import queue
from pathlib import Path
import sys
import os
import locale

# Set Indonesian locale for number formatting
try:
    locale.setlocale(locale.LC_ALL, 'id_ID')
except:
    pass  # Fallback if Indonesian locale not available

# Add parent directory to path for imports
parent_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if parent_dir not in sys.path:
    sys.path.insert(0, parent_dir)

from neurobroV6 import (
    load_tickers, load_free_float, analyze_stock,
    MIN_SCORE, MIN_PRICE, MAX_PRICE, MIN_FREE_FLOAT_PCT, MAX_FREE_FLOAT_PCT,
    MIN_MARKET_CAP, MIN_VAL_TODAY, MIN_AVG_VAL, MIN_AVG_VOLUME
)

warnings.filterwarnings('ignore')

app = Flask(__name__)
app.config['SECRET_KEY'] = 'neurobro-secret-key-change-in-production'

# Custom filter for Indonesian number formatting
@app.template_filter('locale_id_id')
def locale_id_id(value):
    """Format number with Indonesian locale (dots as thousand separator)"""
    if value is None:
        return ''
    try:
        # Format the number with Indonesian locale
        return locale.format_string('%d', int(value), grouping=True)
    except (ValueError, TypeError):
        return str(value)

# Global message queue for SSE
message_queues = []

# Config file path
CONFIG_FILE = os.path.join(os.path.dirname(__file__), 'config.json')

# Load config from file or use defaults
def load_config():
    default_config = {
        'trading_style': 'daytrade',
        'min_score': 85,  # Updated to match user's preference
        'min_price': MIN_PRICE,
        'max_price': MAX_PRICE,
        'min_free_float_pct': MIN_FREE_FLOAT_PCT,
        'max_free_float_pct': MAX_FREE_FLOAT_PCT,
        'min_market_cap': MIN_MARKET_CAP,
        'min_val_today': MIN_VAL_TODAY,
        'min_avg_val': MIN_AVG_VAL,
        'min_avg_volume': MIN_AVG_VOLUME,
        'min_price_range': 5  # Minimum average price range (high - low)
    }

    if os.path.exists(CONFIG_FILE):
        try:
            with open(CONFIG_FILE, 'r') as f:
                loaded_config = json.load(f)
                default_config.update(loaded_config)
                print(f"[CONFIG] Loaded from file: trading_style={loaded_config.get('trading_style', 'daytrade')}, min_score={loaded_config.get('min_score', 85)}")
        except Exception as e:
            print(f"[CONFIG] Error loading config file: {e}")

    return default_config

# Save config to file
def save_config(config_to_save):
    try:
        with open(CONFIG_FILE, 'w') as f:
            json.dump(config_to_save, f, indent=2)
        print(f"[CONFIG] Saved to file: {config_to_save.get('trading_style', 'daytrade')}")
    except Exception as e:
        print(f"[CONFIG] Error saving config file: {e}")

# Dynamic config (loaded from file on startup)
config = load_config()

# Trading styles configuration
TRADING_STYLES = {
    'daytrade': {
        'timeframe': '1H',
        'ma_fast': 9,
        'ma_slow': 21,
        'ma_confirm': 50,
        'ma_type': 'EMA',
        'period': '1y'
    },
    'swing': {
        'timeframe': '1D',
        'ma_fast': 20,
        'ma_slow': 50,
        'ma_confirm': 100,
        'ma_type': 'SMA',
        'period': '1y'
    },
    'long': {
        'timeframe': '1D',
        'ma_fast': 50,
        'ma_slow': 200,
        'ma_confirm': None,
        'ma_type': 'SMA',
        'period': '1y'
    },
    'custom': {
        'timeframe': '1D',
        'ma_fast': 9,
        'ma_slow': 21,
        'ma_confirm': 50,
        'ma_type': 'SMA',
        'period': '1y'
    }
}

# ============================================================
# ROUTES
# ============================================================

@app.route('/')
def index():
    """Dashboard home"""
    return render_template('dashboard.html',
                          config=config,
                          min_score=config['min_score'],
                          min_free_float=config['min_free_float_pct'],
                          max_free_float=config['max_free_float_pct'])


@app.route('/analyze')
def analyze():
    """Single stock analysis page"""
    ticker = request.args.get('ticker', '').upper()
    return render_template('analysis.html', ticker=ticker)


@app.route('/history')
def history():
    """Screening history page"""
    # Get available screening dates
    screening_files = []
    parent_path = Path(parent_dir)

    # Look for screening-*.md files in parent directory
    for file in sorted(parent_path.glob('screening-*.md'), reverse=True):
        # Extract date from filename
        date_str = file.stem.replace('screening-', '')
        try:
            # Parse the date and format it nicely
            # Handle both "YYYY-MM-DD" and "YYYY-MM-DD copy" formats
            if ' copy' in date_str:
                date_str_clean = date_str.replace(' copy', '')
                date_obj = datetime.strptime(date_str_clean, '%Y-%m-%d')
                formatted_date = date_obj.strftime('%d %B %Y') + ' (Copy)'
            else:
                date_obj = datetime.strptime(date_str, '%Y-%m-%d')
                formatted_date = date_obj.strftime('%d %B %Y')

            screening_files.append({
                'date': date_str,
                'formatted': formatted_date,
                'filename': file.name
            })
        except ValueError:
            # Skip if filename doesn't match expected format
            continue

    return render_template('history.html', screening_files=screening_files)


@app.route('/api/history')
def get_history():
    """Get available screening dates"""
    screening_dates = []
    parent_path = Path(parent_dir)

    # Look for screening-*.md files in parent directory
    for file in sorted(parent_path.glob('screening-*.md'), reverse=True):
        # Extract date from filename
        date_str = file.stem.replace('screening-', '')

        # Only include valid YYYY-MM-DD dates (filter out "copy" files etc)
        try:
            datetime.strptime(date_str, '%Y-%m-%d')
            screening_dates.append(date_str)
        except ValueError:
            # Skip invalid date formats
            continue

    return jsonify({'success': True, 'dates': screening_dates})


@app.route('/results/<date>')
def results(date):
    """View specific screening results"""
    results_file = os.path.join(parent_dir, f"screening-{date}.md")

    if not Path(results_file).exists():
        return f"Results for {date} not found", 404

    # Parse markdown file
    with open(results_file, 'r', encoding='utf-8') as f:
        content = f.read()

    # Format date for display
    try:
        date_obj = datetime.strptime(date, '%Y-%m-%d')
        formatted_date = date_obj.strftime('%d %B %Y')
    except ValueError:
        formatted_date = date

    return render_template('results.html',
                          date=date,
                          formatted_date=formatted_date,
                          content=content)


# ============================================================
# API ENDPOINTS
# ============================================================

@app.route('/api/tickers')
def get_tickers():
    """Get list of available tickers"""
    try:
        ticker_file = os.path.join(parent_dir, 'tickers_idx.txt')
        tickers = load_tickers(ticker_file)
        # Remove .JK suffix for display
        tickers_clean = [t.replace('.JK', '') for t in tickers]
        return jsonify({'success': True, 'tickers': tickers_clean})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/config', methods=['GET'])
def get_config():
    """Get current config values"""
    response_config = config.copy()
    response_config['trading_style_info'] = TRADING_STYLES[config['trading_style']]
    return jsonify({'success': True, 'config': response_config})


@app.route('/api/config', methods=['POST'])
def update_config():
    """Update config values"""
    try:
        data = request.json

        # Update only provided values
        if 'trading_style' in data:
            if data['trading_style'] in TRADING_STYLES:
                config['trading_style'] = data['trading_style']
                print(f"[CONFIG] Trading style updated to: {config['trading_style']}")  # Debug
        if 'min_score' in data:
            config['min_score'] = max(0, min(100, int(data['min_score'])))
        if 'min_price' in data:
            config['min_price'] = max(0, int(data['min_price']))
        if 'max_price' in data:
            config['max_price'] = max(0, int(data['max_price']))
        if 'min_free_float' in data:
            config['min_free_float_pct'] = max(0, min(100, int(data['min_free_float'])))
        if 'max_free_float' in data:
            config['max_free_float_pct'] = max(0, min(100, int(data['max_free_float'])))
        if 'min_market_cap' in data:
            config['min_market_cap'] = max(0, int(data['min_market_cap']))
        if 'min_val_today' in data:
            config['min_val_today'] = max(0, int(data['min_val_today']))
        if 'min_avg_val' in data:
            config['min_avg_val'] = max(0, int(data['min_avg_val']))
        if 'min_avg_volume' in data:
            config['min_avg_volume'] = max(0, int(data['min_avg_volume']))
        if 'min_price_range' in data:
            config['min_price_range'] = max(0, int(data['min_price_range']))

        # Save to file for persistence
        save_config(config)

        # Include trading style info in response
        response_config = config.copy()
        response_config['trading_style_info'] = TRADING_STYLES[config['trading_style']]

        return jsonify({'success': True, 'config': response_config})
    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500


@app.route('/api/screen', methods=['POST'])
def run_screen():
    """Run full screening - async with SSE updates"""
    data = request.json
    single_ticker = data.get('ticker')
    golden_cross_only = data.get('golden_cross_only', False)
    ara_only = data.get('ara_only', False)

    if single_ticker:
        tickers = [f"{single_ticker.upper()}.JK"]
    else:
        ticker_file = os.path.join(parent_dir, 'tickers_idx.txt')
        tickers = load_tickers(ticker_file)

    # Start screening in background thread
    def screen_thread():
        try:
            # Create a snapshot of current config to prevent changes during screening
            screening_config = config.copy()
            trading_style = screening_config.get('trading_style', 'daytrade')

            # Check screening mode
            if golden_cross_only:
                publish_sse({'type': 'start', 'total': len(tickers), 'mode': 'golden_cross_only'})
                # Force SMA 20/50 for golden cross detection
                screening_config['trading_style'] = 'swing'  # Uses SMA 20/50
            elif ara_only:
                publish_sse({'type': 'start', 'total': len(tickers), 'mode': 'ara_only'})
            else:
                publish_sse({'type': 'start', 'total': len(tickers)})

            free_float_file = os.path.join(parent_dir, 'free_float_idx.csv')
            free_float_map = load_free_float(free_float_file)

            # Use smaller lists to reduce memory footprint
            qualified = []
            golden_cross_list = []
            ara_list = []

            # Process in batches to reduce memory usage
            batch_size = 50  # Process 50 stocks at a time
            total_batches = (len(tickers) + batch_size - 1) // batch_size

            for batch_num in range(total_batches):
                start_idx = batch_num * batch_size
                end_idx = min(start_idx + batch_size, len(tickers))
                batch_tickers = tickers[start_idx:end_idx]

                for i, ticker in enumerate(batch_tickers, 1):
                    global_idx = start_idx + i
                    result = analyze_stock(ticker, free_float_map, is_single_mode=len(tickers) == 1, config=screening_config)

                    # Publish progress
                    if result:
                        # Check for golden cross
                        has_golden_cross = any('GOLDEN CROSS' in det for det in result.get('details', []))
                        # Check for ARA
                        is_ara = result.get('is_ara', False)

                        if golden_cross_only:
                            # Golden cross only mode - collect all golden cross stocks
                            if has_golden_cross:
                                golden_cross_list.append(result)
                                publish_sse({
                                    'type': 'golden_cross',
                                    'ticker': result['ticker'],
                                    'price': result['price'],
                                    'score': result['score'],
                                    'rsi': result['rsi'],
                                    'entry_low': result['entry_low'],
                                    'entry_high': result['entry_high'],
                                    'sl': result['sl'],
                                    'tp1': result['tp1'],
                                    'rr': result['rr'],
                                    'details': result.get('details', []),
                                    'vol_vs_avg': result.get('vol_vs_avg', 0),
                                    'avg_price_range': result.get('avg_price_range', 0),
                                    'golden_cross_days_ago': result.get('golden_cross_days_ago', None)
                                })
                        elif ara_only:
                            # ARA only mode - collect all ARA stocks
                            if is_ara:
                                ara_list.append(result)
                                publish_sse({
                                    'type': 'ara',
                                    'ticker': result['ticker'],
                                    'price': result['price'],
                                    'score': result['score'],
                                    'rsi': result['rsi'],
                                    'entry_low': result['entry_low'],
                                    'entry_high': result['entry_high'],
                                    'sl': result['sl'],
                                    'tp1': result['tp1'],
                                    'rr': result['rr'],
                                    'is_ara': is_ara,
                                    'ara_pct': result.get('ara_pct', 0),
                                    'ara_type': result.get('ara_type', ''),
                                    'details': result.get('details', []),
                                    'vol_vs_avg': result.get('vol_vs_avg', 0)
                                })
                        else:
                            # Full screening mode
                            if result['score'] >= screening_config['min_score']:
                                qualified.append(result)
                                publish_sse({
                                    'type': 'qualified',
                                    'ticker': result['ticker'],
                                    'price': result['price'],
                                    'score': result['score'],
                                    'rsi': result['rsi'],
                                    'entry_low': result['entry_low'],
                                    'entry_high': result['entry_high'],
                                    'sl': result['sl'],
                                    'tp1': result['tp1'],
                                    'rr': result['rr'],
                                    'is_ara': result.get('is_ara', False),
                                    'ara_pct': result.get('ara_pct', 0),
                                    'details': result.get('details', []),
                                    'vol_vs_avg': result.get('vol_vs_avg', 0)
                                })
                    else:
                        publish_sse({'type': 'failed', 'ticker': ticker.replace('.JK', '')})

                    # Progress update
                    progress = int((global_idx / len(tickers)) * 100)
                    publish_sse({'type': 'progress', 'progress': progress, 'current': global_idx, 'total': len(tickers)})

                # Force garbage collection between batches to free memory
                import gc
                gc.collect()

            # Calculate final counts
            total_analyzed = len(tickers)
            ara_count = len(ara_list) if ara_only else len([r for r in qualified if r.get('is_ara', False)])
            golden_count = len(golden_cross_list) if golden_cross_only else 0

            # Publish completion
            if golden_cross_only:
                # For golden cross only mode, just publish golden cross results
                publish_sse({
                    'type': 'complete',
                    'total_analyzed': total_analyzed,
                    'total_qualified': len(golden_cross_list),
                    'ara_count': ara_count,
                    'golden_count': len(golden_cross_list),
                    'results': [],  # No qualified stocks in golden cross mode
                    'ara_stocks': [],
                    'golden_stocks': golden_cross_list,  # All golden cross stocks
                    'mode': 'golden_cross_only'
                })
            elif ara_only:
                # For ARA only mode, just publish ARA results
                publish_sse({
                    'type': 'complete',
                    'total_analyzed': total_analyzed,
                    'total_qualified': len(ara_list),
                    'ara_count': len(ara_list),
                    'golden_count': 0,
                    'results': [],  # No qualified stocks in ARA mode
                    'ara_stocks': ara_list,  # All ARA stocks
                    'golden_stocks': [],
                    'mode': 'ara_only'
                })
            else:
                # Normal full screening
                ara_stocks = [r for r in qualified if r.get('is_ara', False)]
                golden_cross_stocks = []  # Already filtered in qualified

                publish_sse({
                    'type': 'complete',
                    'total_analyzed': total_analyzed,
                    'total_qualified': len(qualified),
                    'ara_count': len(ara_stocks),
                    'golden_count': len(golden_cross_stocks),
                    'results': qualified[:10],  # Top 10 for display
                    'ara_stocks': ara_stocks[:5],  # Top 5 ARA
                    'golden_stocks': golden_cross_stocks[:10]  # Top 10 Golden Cross Only
                })

        except Exception as e:
            publish_sse({'type': 'error', 'error': str(e)})

    # Start thread
    thread = threading.Thread(target=screen_thread)
    thread.daemon = True
    thread.start()

    return jsonify({'success': True, 'message': 'Screening started'})


@app.route('/api/analyze/<ticker>')
def analyze_single(ticker):
    """Analyze single stock"""
    try:
        ticker_formatted = f"{ticker.upper()}.JK"
        free_float_file = os.path.join(parent_dir, 'free_float_idx.csv')
        free_float_map = load_free_float(free_float_file)

        result = analyze_stock(ticker_formatted, free_float_map, is_single_mode=True, config=config)

        if result:
            return jsonify({'success': True, 'data': result})
        else:
            return jsonify({'success': False, 'error': 'No data available or stock does not meet criteria'}), 404

    except Exception as e:
        import traceback
        error_details = traceback.format_exc()
        print(f"Error analyzing {ticker}: {error_details}")
        return jsonify({'success': False, 'error': f'Analysis failed: {str(e)}'}), 500


@app.route('/api/chart/<ticker>')
def get_chart_data(ticker):
    """Get OHLCV data for chart"""
    try:
        stock = yf.Ticker(f"{ticker.upper()}.JK")
        df = stock.history(period='3mo')

        if df.empty:
            return jsonify({'success': False, 'error': 'No data available'}), 404

        df.columns = [c.lower() for c in df.columns]

        # Prepare data for Chart.js
        dates = df.index.strftime('%Y-%m-%d').tolist()
        ohlcv = {
            'dates': dates,
            'open': df['open'].tolist(),
            'high': df['high'].tolist(),
            'low': df['low'].tolist(),
            'close': df['close'].tolist(),
            'volume': df['volume'].tolist()
        }

        # Calculate indicators
        close = df['close']
        sma20 = close.rolling(window=20).mean()
        sma50 = close.rolling(window=50).mean()
        ema9 = close.ewm(span=9, adjust=False).mean()

        ohlcv['sma20'] = sma20.tolist()
        ohlcv['sma50'] = sma50.tolist()
        ohlcv['ema9'] = ema9.tolist()

        return jsonify({'success': True, 'data': ohlcv})

    except Exception as e:
        return jsonify({'success': False, 'error': str(e)}), 500


# ============================================================
# SSE ENDPOINTS
# ============================================================

@app.route('/stream')
def stream():
    """SSE endpoint for real-time updates"""
    def event_stream():
        q = queue.Queue()
        message_queues.append(q)

        try:
            while True:
                msg = q.get()
                yield f"data: {json.dumps(msg)}\n\n"
        except GeneratorExit:
            message_queues.remove(q)

    return Response(event_stream(), mimetype="text/event-stream")


def publish_sse(message):
    """Publish message to all connected clients"""
    for q in message_queues:
        try:
            q.put(message)
        except:
            pass


# ============================================================
# ERROR HANDLERS
# ============================================================

@app.errorhandler(404)
def not_found(e):
    return jsonify({'success': False, 'error': 'Not found'}), 404


@app.errorhandler(500)
def server_error(e):
    return jsonify({'success': False, 'error': 'Server error'}), 500


if __name__ == '__main__':
    # Disable auto-reload to prevent config reset
    app.run(debug=False, host='0.0.0.0', port=5001)
