import click
from ...api.dravid_api import stream_dravid_api, call_dravid_vision_api
from ...api.dravid_parser import pretty_print_commands
from ...utils.step_executor import Executor
from ...metadata.project_metadata import ProjectMetadataManager
from ...prompts.error_handling import handle_error_with_dravid
from ...utils import print_error, print_success, print_info, print_step, fetch_project_guidelines, run_with_loader
from ...metadata.common_utils import generate_file_description
from .file_operations import get_files_to_modify, get_file_content
from .image_handler import handle_image_query


def execute_dravid_command(query, image_path, debug, instruction_prompt):
    print_info("Starting Dravid CLI tool..")
    print_error("Please make sure you are in a fresh directory.")
    print_info(
        "If it is an existing project, please ensure you're in a git branch")
    print_error("Use Ctrl+C if you're not")

    executor = Executor()
    metadata_manager = ProjectMetadataManager(executor.current_dir)

    try:
        project_context = metadata_manager.get_project_context()

        if project_context:
            print_info("Identifying related files to the query...")
            files_to_modify = run_with_loader(
                lambda: get_files_to_modify(query, project_context),
                "Analyzing project files"
            )

            print_info(
                f"Found {len(files_to_modify)} potentially relevant files.")
            if debug:
                print_info("Possible files to be modified:")
                for file in files_to_modify:
                    print(f"  - {file}")

            print_info("Reading file contents...")
            file_contents = {}
            for file in files_to_modify:
                content = get_file_content(file)
                if content:
                    file_contents[file] = content
                    print_info(f"  - Read content of {file}")

            project_guidelines = fetch_project_guidelines(executor.current_dir)
            file_context = "\n".join(
                [f"Current content of {file}:\n{content}" for file, content in file_contents.items()])
            full_query = f"{project_context}\n\nProject Guidelines:\n{project_guidelines}\n\nCurrent file contents:\n{file_context}\n\nUser query: {query}"
        else:
            print_info(
                "No current project context found. Will create a new project in the current directory.")
            full_query = f"User query: {query}"

        print_info("Preparing to send query to Claude API...")
        if image_path:
            print_info(f"Processing image: {image_path}")
            commands = run_with_loader(
                lambda: call_dravid_vision_api(
                    full_query, image_path, include_context=True, instruction_prompt=instruction_prompt),
                "Analyzing image and generating response"
            )
        else:
            print_info("Streaming response from Claude API...")
            commands = []
            for new_commands in stream_dravid_api(full_query, include_context=True, instruction_prompt=instruction_prompt):
                commands.extend(new_commands)
                print_info(f"Received {len(new_commands)} new command(s)")
                pretty_print_commands(new_commands)

        if not commands:
            print_error(
                "Failed to parse Claude's response or no commands to execute.")
            return

        print_info(f"Parsed {len(commands)} commands from Claude's response.")

        for i, cmd in enumerate(commands):
            if cmd['type'] == 'explanation':
                print_info(f"Explanation: {cmd['content']}")
                continue

            print_step(i+1, len(commands),
                       f"Processing {cmd['type']} command...")

            max_retries = 3
            for attempt in range(max_retries):
                if cmd['type'] == 'metadata':
                    if cmd['operation'] == 'UPDATE_DEV_SERVER':
                        metadata_manager.update_dev_server_info(
                            cmd['start_command'],
                            cmd['framework'],
                            cmd['language']
                        )
                        print_success(
                            "Updated dev server info in project metadata.")
                        break
                    elif cmd['operation'] == 'UPDATE_FILE':
                        if metadata_manager.update_metadata_from_file(cmd['filename']):
                            print_success(
                                f"Updated metadata for file: {cmd['filename']}")
                        else:
                            print_error(
                                f"Failed to update metadata for file: {cmd['filename']}")
                        break
                else:
                    result = handle_command(cmd, executor, metadata_manager)
                    if isinstance(result, tuple) and not result[0]:
                        error = result[1]
                        print_info(
                            f"Error occurred (Attempt {attempt+1}/{max_retries}). Attempting to fix with Claude's assistance.")
                        if handle_error_with_dravid(error, cmd, executor, metadata_manager):
                            print_info(
                                "Fix applied successfully. Retrying the original command.")
                        else:
                            print_error(
                                f"Unable to fix the error after attempt {attempt+1}.")
                            if attempt == max_retries - 1:
                                print_info(
                                    "Max retries reached. Skipping this command.")
                                break
                    else:
                        break  # Command executed successfully
            else:
                print_error(
                    f"Failed to execute command after {max_retries} attempts. Skipping and moving to the next command.")

        print_success("Dravid CLI tool execution completed.")
    except Exception as e:
        print_error(f"An unexpected error occurred: {str(e)}")
        if debug:
            import traceback
            traceback.print_exc()


def handle_command(cmd, executor, metadata_manager):
    try:
        if cmd['type'] == 'shell':
            output = executor.execute_shell_command(cmd['command'])
            if output is not None:
                print_success(f"Executed shell command: {cmd['command']}")
                if output:
                    click.echo(f"Command output:\n{output}")
            else:
                raise Exception(
                    f"Shell command execution failed: {cmd['command']}")
        elif cmd['type'] == 'file':
            operation_performed = executor.perform_file_operation(
                cmd['operation'],
                cmd['filename'],
                cmd.get('content'),
                force=True
            )

            if operation_performed:
                project_context = metadata_manager.get_project_context()
                folder_structure = executor.get_folder_structure()
                file_type, description = run_with_loader(
                    lambda: generate_file_description(
                        cmd['filename'],
                        cmd.get('content', ''),
                        project_context,
                        folder_structure
                    ),
                    f"Generating description for {cmd['filename']}"
                )
                metadata_manager.update_file_metadata(
                    cmd['filename'],
                    file_type,
                    cmd.get('content', ''),
                    description
                )
                print_success(
                    f"Performed {cmd['operation']} operation on file: {cmd['filename']}")
                print_info(f"Generated description: {description}")
            else:
                raise Exception(
                    f"File operation failed: {cmd['operation']} on {cmd['filename']}")
        return True
    except Exception as e:
        print_error(f"Error in handle_command: {str(e)}")
        return False, e
