from flask import Blueprint, render_template, request, redirect, url_for, current_app
from extensions import db
# from flask_login import login_required, current_user # Add if authentication is needed
from jinja2_fragments import render_block # Import render_block

programs_bp = Blueprint('programs', __name__, url_prefix='/programs')

from flask import flash # Import flash for displaying messages

@programs_bp.route('/create', methods=['GET', 'POST'])
# @login_required # Uncomment if login is required
def create_program():
    if request.method == 'POST':
        program_name = request.form.get('program_name', '').strip()
        description = request.form.get('description', '').strip()
        sessions_data = []
        i = 0
        while True:
            # Check for the presence of session order to determine if the session exists
            session_order_key = f'session_order_{i}'
            if session_order_key not in request.form:
                break # No more sessions

            session_order = request.form.get(session_order_key)
            session_name = request.form.get(f'session_name_{i}', '').strip()
            # Get list of selected exercise IDs for this session
            exercise_ids_str = request.form.getlist(f'exercises_{i}')

            # Basic validation for session data
            if not exercise_ids_str or not session_order:
                 flash(f"Error processing session {i+1}: Missing exercises or order.", "error")
                 # TODO: Re-render form preserving entered data
                 return redirect(url_for('programs.create_program'))

            try:
                # Convert exercise IDs to integers and sort them for consistent filter generation
                exercise_ids = sorted([int(eid) for eid in exercise_ids_str])

                sessions_data.append({
                    'order': int(session_order),
                    'name': session_name if session_name else None, # Store None if empty
                    'exercise_ids': exercise_ids # Store the list of exercise IDs
                })
            except ValueError:
                 flash(f"Error processing session {i+1}: Invalid exercise ID or order.", "error")
                 return redirect(url_for('programs.create_program'))

            i += 1

        # --- Validation ---
        if not program_name:
            flash("Program Name is required.", "error")
            # TODO: Re-render form preserving entered data
            return redirect(url_for('programs.create_program'))
        if not sessions_data:
            flash("At least one session must be added.", "error")
            # TODO: Re-render form preserving entered data
            return redirect(url_for('programs.create_program'))

        # --- Database Insertion ---
        try:
            # Insert Program
            program_result = db.execute(
                "INSERT INTO workout_program (name, description) VALUES (%s, %s) RETURNING program_id",
                [program_name, description if description else None],
                commit=True, one=True
            )
            if not program_result or 'program_id' not in program_result:
                 raise Exception("Failed to create workout program entry.")

            new_program_id = program_result['program_id']

            # Insert Sessions (and find/create tags)
            for session in sessions_data:
                # 1. Generate the canonical filter string from sorted exercise IDs
                if not session['exercise_ids']:
                    flash(f"Session {session['order']} must have at least one exercise selected.", "error")
                    # Ideally, rollback program insert or handle differently
                    return redirect(url_for('programs.create_program'))

                tag_filter = "?" + "&".join(f"exercise_id={eid}" for eid in session['exercise_ids'])
                tag_name = session['name'] if session['name'] else f"Program Session {session['order']} Exercises" # Default tag name

                # 2. Find existing tag with this exact filter (non-person specific)
                existing_tag = db.execute(
                    "SELECT tag_id FROM tag WHERE filter = %s AND person_id IS NULL",
                    [tag_filter], one=True
                )

                session_tag_id = None
                if existing_tag:
                    session_tag_id = existing_tag['tag_id']
                    # Optional: Update tag name if session name provided and different?
                    # db.execute("UPDATE tag SET name = %s WHERE tag_id = %s", [tag_name, session_tag_id], commit=True)
                else:
                    # 3. Create new tag if not found
                    # Ensure tag name uniqueness if desired (e.g., append number if name exists)
                    # For simplicity, allow duplicate names for now, rely on filter for uniqueness
                    new_tag_result = db.execute(
                        "INSERT INTO tag (name, filter, person_id) VALUES (%s, %s, NULL) RETURNING tag_id",
                        [tag_name, tag_filter], commit=True, one=True
                    )
                    if not new_tag_result or 'tag_id' not in new_tag_result:
                        raise Exception(f"Failed to create tag for session {session['order']}.")
                    session_tag_id = new_tag_result['tag_id']

                # 4. Insert program_session using the found/created tag_id
                db.execute(
                    """INSERT INTO program_session (program_id, session_order, session_name, tag_id)
                       VALUES (%s, %s, %s, %s)""",
                    [new_program_id, session['order'], session['name'], session_tag_id],
                    commit=True # Commit each session insert
                )

            flash(f"Workout Program '{program_name}' created successfully!", "success")
            # TODO: Redirect to a program view page once it exists
            # return redirect(url_for('programs.view_program', program_id=new_program_id))
            return redirect(url_for('programs.list_programs')) # Redirect to a list page for now

        except Exception as e:
            # Log the error e
            print(f"Error creating program: {e}") # Basic logging
            flash(f"Database error creating program: {e}", "error")
            # Rollback might be needed if using transactions across inserts
            return redirect(url_for('programs.create_program'))

    else: # GET Request
        # Fetch all available exercises to populate multi-selects
        exercises = db.execute("SELECT exercise_id, name FROM exercise ORDER BY name")
        if exercises is None:
            exercises = [] # Ensure exercises is an iterable

        # Pass exercises to the template context
        return render_template('program_create.html', exercises=exercises, render_block=render_block) # Pass exercises instead of tags


from flask_htmx import HTMX # Import HTMX

htmx = HTMX() # Initialize HTMX if not already done globally

# Placeholder for program list route (used in POST redirect)
@programs_bp.route('/', methods=['GET'])
# @login_required
def list_programs():
    # Fetch and display list of programs
    programs = db.execute("SELECT program_id, name, description FROM workout_program ORDER BY created_at DESC")
    if programs is None:
        programs = []

    # Check if it's an HTMX request
    if htmx:
        # Render only the content block for HTMX requests
        return render_block(current_app.jinja_env, 'program_list.html', 'content', programs=programs)
    else:
        # Render the full page for regular requests
        return render_template('program_list.html', programs=programs)


@programs_bp.route('/<int:program_id>/delete', methods=['DELETE'])
# @login_required # Add authentication if needed
def delete_program(program_id):
    """Deletes a workout program and its associated sessions/assignments."""
    try:
        # The ON DELETE CASCADE constraint on program_session and person_program_assignment
        # should handle deleting related rows automatically when the program is deleted.
        result = db.execute(
            "DELETE FROM workout_program WHERE program_id = %s RETURNING program_id",
            [program_id],
            commit=True, one=True
        )
        if result and result.get('program_id') == program_id:
             # Return empty response for HTMX, maybe trigger list refresh
             # flash(f"Program ID {program_id} deleted successfully.", "success") # Flash might not show on empty response
             response = "" # Empty response indicates success to HTMX
             headers = {"HX-Trigger": "programDeleted"} # Trigger event for potential list refresh
             return response, 200, headers
        else:
             # Program not found or delete failed silently
             flash(f"Could not find or delete program ID {program_id}.", "error")
             # Returning an error status might be better for HTMX error handling
             return "Error: Program not found or deletion failed", 404

    except Exception as e:
        # Log the error e
        print(f"Error deleting program {program_id}: {e}")
        flash(f"Database error deleting program: {e}", "error")
        # Return an error status for HTMX
        return "Server error during deletion", 500


# TODO: Add routes for viewing, editing, and assigning programs
from urllib.parse import parse_qs # Needed to parse tag filters

@programs_bp.route('/<int:program_id>', methods=['GET'])
# @login_required
def view_program(program_id):
    """Displays the details of a specific workout program."""
    # Fetch program details
    program = db.execute(
        "SELECT program_id, name, description, created_at FROM workout_program WHERE program_id = %s",
        [program_id], one=True
    )
    if not program:
        flash(f"Workout Program with ID {program_id} not found.", "error")
        return redirect(url_for('programs.list_programs'))

    # Fetch sessions and their associated tags
    sessions = db.execute(
        """
        SELECT
            ps.session_id, ps.session_order, ps.session_name,
            t.tag_id, t.name as tag_name, t.filter as tag_filter
        FROM program_session ps
        JOIN tag t ON ps.tag_id = t.tag_id
        WHERE ps.program_id = %s
        ORDER BY ps.session_order ASC
        """,
        [program_id]
    )

    # Process sessions to extract exercise IDs and fetch exercise names
    sessions_with_exercises = []
    if sessions:
        for session in sessions:
            exercise_ids = []
            if session.get('tag_filter'):
                # Parse the filter string (e.g., "?exercise_id=5&exercise_id=1009")
                parsed_filter = parse_qs(session['tag_filter'].lstrip('?'))
                exercise_ids_str = parsed_filter.get('exercise_id', [])
                try:
                    # Ensure IDs are unique and sorted if needed, though order might matter from filter
                    exercise_ids = sorted(list(set(int(eid) for eid in exercise_ids_str)))
                except ValueError:
                    print(f"Warning: Could not parse exercise IDs from filter for tag {session['tag_id']}: {session['tag_filter']}")
                    exercise_ids = [] # Handle parsing error gracefully

            exercises = []
            if exercise_ids:
                # Fetch exercise details for the extracted IDs
                # Using tuple() for IN clause compatibility
                # Ensure tuple has at least one element for SQL IN clause
                if len(exercise_ids) == 1:
                    exercises_tuple = (exercise_ids[0],) # Comma makes it a tuple
                else:
                    exercises_tuple = tuple(exercise_ids)

                exercises = db.execute(
                    "SELECT exercise_id, name FROM exercise WHERE exercise_id IN %s ORDER BY name",
                    [exercises_tuple]
                )
                if exercises is None: exercises = [] # Ensure it's iterable

            sessions_with_exercises.append({
                **session, # Include all original session/tag data
                'exercises': exercises
            })

    # Prepare context for the template
    context = {
        'program': program,
        'sessions': sessions_with_exercises
    }

    # Check for HTMX request (optional, for potential future use)
    if htmx:
        # Assuming you have a block named 'content' in program_view.html
        return render_block(current_app.jinja_env, 'program_view.html', 'content', **context)
    else:
        return render_template('program_view.html', **context)

# TODO: Add routes for editing and assigning programs