import json
import os
import re
import logging
from flask import render_template, request, redirect, url_for, flash, session, current_app
from models import db, Character, Preset, Outfit, Action, Style, Scene, Detailer, Checkpoint, Look
from sqlalchemy.orm.attributes import flag_modified
from services.sync import sync_presets
from services.generation import generate_from_preset
from services.llm import load_prompt, call_llm
from routes.shared import register_common_routes, apply_library_filters

logger = logging.getLogger('gaze')


def register_routes(app):
    register_common_routes(app, 'presets')

    @app.route('/presets')
    def presets_index():
        presets, fav, nsfw = apply_library_filters(Preset.query, Preset)
        return render_template('presets/index.html', presets=presets,
                               favourite_filter=fav, nsfw_filter=nsfw)

    @app.route('/preset/<path:slug>')
    def preset_detail(slug):
        preset = Preset.query.filter_by(slug=slug).first_or_404()
        preview_path = session.get(f'preview_preset_{slug}')
        extra_positive = session.get(f'extra_pos_preset_{slug}', '')
        extra_negative = session.get(f'extra_neg_preset_{slug}', '')
        return render_template('presets/detail.html', preset=preset, preview_path=preview_path,
                               extra_positive=extra_positive, extra_negative=extra_negative)

    @app.route('/preset/<path:slug>/generate', methods=['POST'])
    def generate_preset_image(slug):
        preset = Preset.query.filter_by(slug=slug).first_or_404()

        try:
            action = request.form.get('action', 'preview')
            extra_positive = request.form.get('extra_positive', '').strip()
            extra_negative = request.form.get('extra_negative', '').strip()

            # Save to session (web UI state)
            session[f'extra_pos_preset_{slug}'] = extra_positive
            session[f'extra_neg_preset_{slug}'] = extra_negative
            session.modified = True

            # Build overrides from form
            seed_val = request.form.get('seed', '').strip()
            form_width = request.form.get('width', '').strip()
            form_height = request.form.get('height', '').strip()

            overrides = {
                'action': action,
                'extra_positive': extra_positive,
                'extra_negative': extra_negative,
                'checkpoint': request.form.get('checkpoint_override', '').strip(),
                'seed': int(seed_val) if seed_val else None,
                'width': int(form_width) if form_width else None,
                'height': int(form_height) if form_height else None,
            }

            job = generate_from_preset(preset, overrides)

            session[f'preview_preset_{slug}'] = None
            session.modified = True

            if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                return {'status': 'queued', 'job_id': job['id']}
            return redirect(url_for('preset_detail', slug=slug))

        except Exception as e:
            logger.exception("Generation error (preset %s): %s", slug, e)
            if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
                return {'error': str(e)}, 500
            flash(f"Error during generation: {str(e)}")
            return redirect(url_for('preset_detail', slug=slug))

    @app.route('/preset/<path:slug>/edit', methods=['GET', 'POST'])
    def edit_preset(slug):
        preset = Preset.query.filter_by(slug=slug).first_or_404()
        if request.method == 'POST':
            name = request.form.get('preset_name', preset.name)
            preset.name = name

            def _tog(val):
                """Convert form value ('true'/'false'/'random') to JSON toggle value."""
                if val == 'random':
                    return 'random'
                return val == 'true'

            def _entity_id(val):
                return val if val else None

            char_id = _entity_id(request.form.get('char_character_id'))
            new_data = {
                'preset_id': preset.preset_id,
                'preset_name': name,
                'character': {
                    'character_id': char_id,
                    'use_lora': request.form.get('char_use_lora') == 'on',
                    'fields': {
                        'identity': {k: _tog(request.form.get(f'id_{k}', 'true'))
                                     for k in ['base', 'head', 'upper_body', 'lower_body', 'hands', 'feet', 'additional']},
                        'defaults': {k: _tog(request.form.get(f'def_{k}', 'false'))
                                     for k in ['expression', 'pose', 'scene']},
                        'wardrobe': {
                            'outfit': request.form.get('wardrobe_outfit', 'default') or 'default',
                            'fields': {k: _tog(request.form.get(f'wd_{k}', 'true'))
                                       for k in ['base', 'head', 'upper_body', 'lower_body', 'hands', 'feet', 'additional']},
                        },
                    },
                },
                'outfit':   {'outfit_id': _entity_id(request.form.get('outfit_id')),
                             'use_lora': request.form.get('outfit_use_lora') == 'on'},
                'action':   {'action_id': _entity_id(request.form.get('action_id')),
                             'use_lora': request.form.get('action_use_lora') == 'on',
                             'suppress_wardrobe': {'true': True, 'false': False, 'random': 'random'}.get(
                                 request.form.get('act_suppress_wardrobe')),
                             'fields': {k: _tog(request.form.get(f'act_{k}', 'true'))
                                        for k in ['base', 'head', 'upper_body', 'lower_body', 'hands', 'feet', 'additional']}},
                'style':    {'style_id': _entity_id(request.form.get('style_id')),
                             'use_lora': request.form.get('style_use_lora') == 'on'},
                'scene':    {'scene_id': _entity_id(request.form.get('scene_id')),
                             'use_lora': request.form.get('scene_use_lora') == 'on',
                             'fields': {k: _tog(request.form.get(f'scn_{k}', 'true'))
                                        for k in ['background', 'foreground', 'furniture', 'colors', 'lighting', 'theme']}},
                'detailer': {'detailer_id': _entity_id(request.form.get('detailer_id')),
                             'use_lora': request.form.get('detailer_use_lora') == 'on'},
                'look':     {'look_id': _entity_id(request.form.get('look_id'))},
                'checkpoint': {'checkpoint_path': _entity_id(request.form.get('checkpoint_path'))},
                'resolution': {
                    'width': int(request.form.get('res_width', 1024)),
                    'height': int(request.form.get('res_height', 1024)),
                    'random': request.form.get('res_random') == 'on',
                },
                'tags': [t.strip() for t in request.form.get('tags', '').split(',') if t.strip()],
            }

            preset.data = new_data
            flag_modified(preset, "data")
            db.session.commit()

            if preset.filename:
                file_path = os.path.join(current_app.config['PRESETS_DIR'], preset.filename)
                with open(file_path, 'w') as f:
                    json.dump(new_data, f, indent=2)

            flash('Preset saved!')
            return redirect(url_for('preset_detail', slug=slug))

        characters  = Character.query.order_by(Character.name).all()
        outfits     = Outfit.query.order_by(Outfit.name).all()
        actions     = Action.query.order_by(Action.name).all()
        styles      = Style.query.order_by(Style.name).all()
        scenes      = Scene.query.order_by(Scene.name).all()
        detailers   = Detailer.query.order_by(Detailer.name).all()
        looks       = Look.query.order_by(Look.name).all()
        checkpoints = Checkpoint.query.order_by(Checkpoint.name).all()
        return render_template('presets/edit.html', preset=preset,
                               characters=characters, outfits=outfits, actions=actions,
                               styles=styles, scenes=scenes, detailers=detailers,
                               looks=looks, checkpoints=checkpoints)

    @app.route('/presets/rescan', methods=['POST'])
    def rescan_presets():
        sync_presets()
        flash('Preset library synced.')
        return redirect(url_for('presets_index'))

    @app.route('/preset/create', methods=['GET', 'POST'])
    def create_preset():
        form_data = {}

        if request.method == 'POST':
            name = request.form.get('name', '').strip()
            description = request.form.get('description', '').strip()
            use_llm = request.form.get('use_llm') == 'on'

            form_data = {'name': name, 'description': description, 'use_llm': use_llm}

            safe_id = re.sub(r'[^a-zA-Z0-9]+', '_', name.lower()).strip('_') or 'preset'
            safe_slug = re.sub(r'[^a-zA-Z0-9_]', '', safe_id)
            base_id = safe_id
            counter = 1
            while os.path.exists(os.path.join(current_app.config['PRESETS_DIR'], f"{safe_id}.json")):
                safe_id = f"{base_id}_{counter}"
                safe_slug = re.sub(r'[^a-zA-Z0-9_]', '', safe_id)
                counter += 1

            if use_llm and description:
                system_prompt = load_prompt('preset_system.txt')
                if not system_prompt:
                    flash('Preset system prompt file not found.', 'error')
                    return render_template('presets/create.html', form_data=form_data)
                try:
                    llm_response = call_llm(
                        f"Create a preset profile named '{name}' based on this description: {description}",
                        system_prompt
                    )
                    clean_json = llm_response.replace('```json', '').replace('```', '').strip()
                    preset_data = json.loads(clean_json)
                except Exception as e:
                    logger.exception("LLM error creating preset: %s", e)
                    flash(f"AI generation failed: {e}", 'error')
                    return render_template('presets/create.html', form_data=form_data)
            else:
                preset_data = {
                    'character': {'character_id': 'random', 'use_lora': True,
                                  'fields': {
                                      'identity': {k: True for k in ['base', 'head', 'upper_body', 'lower_body', 'hands', 'feet', 'additional']},
                                      'defaults': {k: False for k in ['expression', 'pose', 'scene']},
                                      'wardrobe': {'outfit': 'default',
                                                   'fields': {k: True for k in ['base', 'head', 'upper_body', 'lower_body', 'hands', 'feet', 'additional']}},
                                  }},
                    'outfit':   {'outfit_id': None, 'use_lora': True},
                    'action':   {'action_id': None, 'use_lora': True,
                                 'fields': {k: True for k in ['base', 'head', 'upper_body', 'lower_body', 'hands', 'feet', 'additional']}},
                    'style':    {'style_id': None, 'use_lora': True},
                    'scene':    {'scene_id': None, 'use_lora': True,
                                 'fields': {k: True for k in ['background', 'foreground', 'furniture', 'colors', 'lighting', 'theme']}},
                    'detailer': {'detailer_id': None, 'use_lora': True},
                    'look':     {'look_id': None},
                    'checkpoint': {'checkpoint_path': None},
                    'resolution': {'width': 1024, 'height': 1024, 'random': False},
                    'tags': [],
                }

            preset_data['preset_id'] = safe_id
            preset_data['preset_name'] = name

            os.makedirs(current_app.config['PRESETS_DIR'], exist_ok=True)
            file_path = os.path.join(current_app.config['PRESETS_DIR'], f"{safe_id}.json")
            with open(file_path, 'w') as f:
                json.dump(preset_data, f, indent=2)

            new_preset = Preset(preset_id=safe_id, slug=safe_slug,
                                filename=f"{safe_id}.json", name=name, data=preset_data)
            db.session.add(new_preset)
            db.session.commit()
            flash(f"Preset '{name}' created!")
            return redirect(url_for('edit_preset', slug=safe_slug))

        return render_template('presets/create.html', form_data=form_data)

