# core/views.py

from django.shortcuts import render, redirect, get_object_or_404
from django.http import JsonResponse
from django.views.decorators.http import require_POST
import json
import logging
import threading
from .models import Article, TodoItem, Conversation, Message, Setting
from .forms import TodoItemForm
import time
from ai.local_ai_api import LocalAIApi

# Get an instance of a logger
logger = logging.getLogger(__name__)


def index(request):
    if request.method == 'POST':
        form = TodoItemForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('core:index')
    else:
        form = TodoItemForm()
    
    todo_list = TodoItem.objects.all().order_by('-created_at')
    articles = Article.objects.all()
    
    context = {
        'articles': articles,
        'todo_list': todo_list,
        'form': form,
        'timestamp': int(time.time()),
    }
    return render(request, "core/index.html", context)


def kanban_board(request):
    tasks = TodoItem.objects.all().order_by('created_at')
    tasks_by_status = {
        status_value: list(filter(lambda t: t.status == status_value, tasks))
        for status_value, status_display in TodoItem.STATUS_CHOICES
    }

    context = {
        'tasks_by_status': tasks_by_status,
        'status_choices': TodoItem.STATUS_CHOICES,
        'timestamp': int(time.time()),
    }
    return render(request, "core/kanban.html", context)


def article_detail(request, article_id):
    article = Article.objects.get(pk=article_id)
    return render(request, "core/article_detail.html", {"article": article})


@require_POST
def update_task_status(request):
    try:
        data = json.loads(request.body)
        task_id = data.get('task_id')
        new_status = data.get('new_status')

        task = get_object_or_404(TodoItem, id=task_id)
        task.status = new_status
        task.save()

        return JsonResponse({'success': True})
    except (json.JSONDecodeError, TypeError, ValueError) as e:
        return JsonResponse({'success': False, 'error': str(e)}, status=400)


@require_POST
def delete_task(request, task_id):
    task = get_object_or_404(TodoItem, id=task_id)
    task.delete()
    
    # Redirect to the previous page or a default URL
    referer = request.META.get('HTTP_REFERER')
    if referer:
        return redirect(referer)
    return redirect('core:index')


@require_POST
def cleanup_tasks(request):
    TodoItem.objects.all().delete()
    return redirect('core:index')


def execute_command(command_data):
    command_name = command_data.get('name')
    args = command_data.get('args', {})
    logger.info(f"Executing command: {command_name} with args: {args}")

    try:
        if command_name == 'send_message':
            message = args.get('message')
            if not message:
                logger.error("Command 'send_message' failed: 'message' is a required argument.")
                return "[SYSTEM] Error: 'message' is a required argument for send_message."
            return message

        elif command_name == 'add_task':
            title = args.get('title')
            if not title:
                logger.error("Command 'add_task' failed: 'title' is a required argument.")
                return "[SYSTEM] Error: 'title' is a required argument for add_task."
            
            new_task = TodoItem.objects.create(
                title=title,
                description=args.get('description', ''),
                status=args.get('status', 'todo'),
                tags=args.get('tags', ''),
                deadline=args.get('deadline')
            )
            logger.info(f"Command 'add_task' executed successfully. New task ID: {new_task.id}")
            return f"[SYSTEM] Command 'add_task' executed successfully. A new task with ID {new_task.id}, title '{new_task.title}', description '{new_task.description}', status '{new_task.status}', tags '{new_task.tags}', and deadline '{new_task.deadline}' has been created."

        elif command_name == 'edit_task':
            task_id = args.get('task_id')
            if not task_id:
                logger.error("Command 'edit_task' failed: 'task_id' is a required argument.")
                return "[SYSTEM] Error: 'task_id' is a required argument for edit_task."

            task = get_object_or_404(TodoItem, id=task_id)
            
            if 'title' in args:
                task.title = args['title']
            if 'description' in args:
                task.description = args['description']
            if 'status' in args:
                task.status = args['status']
            if 'tags' in args:
                task.tags = args['tags']
            if 'deadline' in args:
                task.deadline = args['deadline']
            
            task.save()
            logger.info(f"Command 'edit_task' for task ID {task_id} executed successfully.")
            return f"[SYSTEM] Command 'edit_task' for task ID {task_id} executed successfully. The task now has title '{task.title}', description '{task.description}', status '{task.status}', tags '{task.tags}', and deadline '{task.deadline}'."

        elif command_name == 'delete_task':
            task_id = args.get('task_id')
            if not task_id:
                logger.error("Command 'delete_task' failed: 'task_id' is a required argument.")
                return "[SYSTEM] Error: 'task_id' is a required argument for delete_task."

            task = get_object_or_404(TodoItem, id=task_id)
            task.delete()
            logger.info(f"Command 'delete_task' for task ID {task_id} executed successfully.")
            return f"[SYSTEM] Command 'delete_task' for task ID {task_id} executed successfully."

        else:
            logger.warning(f"Unknown command received: '{command_name}'")
            return f"[SYSTEM] Error: Unknown command '{command_name}'."
    except Exception as e:
        logger.error(f"Error executing command '{command_name}': {e}", exc_info=True)
        return f"[SYSTEM] Error executing command '{command_name}': {e}"


def run_ai_process_in_background(conversation_id):
    """This function runs in a separate thread."""
    try:
        conversation = get_object_or_404(Conversation, id=conversation_id)
        conversation.is_generating = True
        conversation.save()

        history = []
        for msg in conversation.messages.order_by('created_at'):
            role = msg.sender
            if role == 'ai':
                role = 'assistant'
            # User messages are already 'user', system messages are 'user' for the model
            elif role == 'system':
                role = 'user'
            history.append({"role": role, "content": msg.content})

        custom_instructions, _ = Setting.objects.get_or_create(
            key='custom_instructions',
            defaults={'value': ''}
        )
        custom_instructions_text = custom_instructions.value + '''\n\n''' if custom_instructions.value else ''
        
        system_message = {
            "role": "system",
            "content": custom_instructions_text + '''You are a project management assistant. To communicate with the user, you MUST use the `send_message` command.

**Commands must be in a specific JSON format.** Your response must be a JSON object with the following structure:

```json
{
  "command": {
    "name": "command_name",
    "args": {
      "arg1": "value1",
      "arg2": "value2"
    }
  }
}
```

**Available Commands:**

*   `send_message`: Sends a message to the user. **USE THIS FOR ALL CONVERSATIONAL RESPONSES.**
    *   `args`:
        *   `message` (string, required): The message to send to the user.
*   `add_task`: Adds a new task.
    *   `args`:
        *   `title` (string, required): The title of the task.
        *   `description` (string, optional): A detailed description of the task.
        *   `status` (string, optional, default: 'todo'): The status of the task. Valid options are 'todo', 'inprogress', 'done', 'blocked'.
        *   `tags` (string, optional): A comma-separated string of tags (e.g., "work,urgent").
        *   `deadline` (string, optional): The deadline for the task in YYYY-MM-DD format.
*   `edit_task`: Edits an existing task.
    *   `args`:
        *   `task_id` (integer, required): The ID of the task to edit.
        *   `title` (string, optional): The new title.
        *   `description` (string, optional): The new description.
        *   `status` (string, optional): The new status. Valid options are 'todo', 'inprogress', 'done', 'blocked'.
        *   `tags` (string, optional): The new comma-separated string of tags.
        *   `deadline` (string, optional): The new deadline for the task in YYYY-MM-DD format.
*   `delete_task`: Deletes a task.
    *   `args`:
        *   `task_id` (integer, required): The ID of the task to delete.

**Execution Loop:**

1.  You can issue a series of commands to be executed sequentially.
2.  The system executes each command and provides a result.
3.  The loop will stop if you call `send_message` or after a maximum of 31 iterations.

**VERY IMPORTANT:**
- To talk to the user, you MUST use the `send_message` command. This command will STOP the execution loop.
- If you need to perform multiple actions (e.g., add a task and then comment on it), issue the action commands (`add_task`, `edit_task`, etc.) *before* using `send_message`.
- ONLY use commands other than `send_message` if the user explicitly asks you to `add`, `edit`, or `delete` tasks.

**Examples:**

*   **User:** "Hi, how are you?"
    *   **Correct AI Response:**
        ```json
        {
          "command": {
            "name": "send_message",
            "args": {
              "message": "I'm doing great, thanks for asking! How can I help you with your tasks today?"
            }
          }
        }
        ```
*   **User:** "add a new task to buy milk with tag 'personal'"
    *   **Correct AI Response:**
        ```json
        {
          "command": {
            "name": "add_task",
            "args": {
              "title": "buy milk",
              "tags": "personal"
            }
          }
        }
        ```

**IMPORTANT:** Do not wrap the JSON command in markdown backticks or any other text. The entire response must be the JSON object.'''
        }

        tasks = TodoItem.objects.all().order_by('created_at')
        task_list = []
        for task in tasks:
            deadline_str = f", Deadline: {task.deadline}" if task.deadline else ""
            task_list.append(f"- ID {task.id}: {task.title} (Status: {task.get_status_display()}, Tags: {task.tags or 'None'}{deadline_str})")
        task_list_str = "\n".join(task_list)
        
        tasks_context = {
            "role": "system",
            "content": f"Here is the current list of tasks:\n{task_list_str}"
        }

        logger.info("Starting AI processing loop...")
        
        for i in range(31): # Loop up to 31 times
            logger.info(f"AI loop iteration {i+1}")
            
            response = LocalAIApi.create_response({
                "input": [
                    system_message,
                    tasks_context,
                ] + history,
                "text": {"format": {"type": "json_object"}},
            })

            if not response.get("success"):
                logger.error(f"AI API request failed. Full error: {response.get('error')}")
                ai_text = "I couldn't process that. Please try again."
                Message.objects.create(conversation=conversation, content=ai_text, sender='ai')
                break
            
            ai_text = LocalAIApi.extract_text(response)
            if not ai_text:
                logger.warning("AI response was empty.")
                ai_text = "I couldn't process that. Please try again."
                Message.objects.create(conversation=conversation, content=ai_text, sender='ai')
                break
            
            try:
                command_json = json.loads(ai_text)
                if 'command' in command_json:
                    command_name = command_json.get('command', {}).get('name')
                    command_result = execute_command(command_json['command'])
                    
                    sender = 'ai' if command_name == 'send_message' else 'system'
                    Message.objects.create(conversation=conversation, content=command_result, sender=sender)
                    
                    if command_name == 'send_message':
                        break 
                    
                    history.append({"role": "user", "content": command_result})
                else:
                    Message.objects.create(conversation=conversation, content=ai_text, sender='ai')
                    break
            except (json.JSONDecodeError, TypeError):
                Message.objects.create(conversation=conversation, content=ai_text, sender='ai')
                break
        else:
            logger.warning("AI loop finished after 31 iterations without sending a message.")
            final_message = "I seem to be stuck in a loop. Could you clarify what you'd like me to do?"
            Message.objects.create(conversation=conversation, content=final_message, sender='ai')

    except Exception as e:
        logger.error(f"An unexpected error occurred in background AI process: {e}", exc_info=True)
        try:
            # Try to inform the user about the error
            Message.objects.create(conversation_id=conversation_id, content=f"An internal error occurred: {str(e)}", sender='ai')
        except Exception as e2:
            logger.error(f"Could not even save the error message to the conversation: {e2}", exc_info=True)

    finally:
        # Ensure is_generating is always set to False
        try:
            conversation = Conversation.objects.get(id=conversation_id)
            conversation.is_generating = False
            conversation.save()
        except Conversation.DoesNotExist:
            logger.error(f"Conversation with ID {conversation_id} does not exist when trying to finalize background process.")
        except Exception as e:
            logger.error(f"Could not finalize background process for conversation {conversation_id}: {e}", exc_info=True)


def chat_view(request, conversation_id=None):
    if request.method == 'POST':
        # Create a new conversation
        if 'title' in request.POST:
            title = request.POST.get('title', 'New Conversation').strip()
            if not title:
                title = 'New Conversation'
            conversation = Conversation.objects.create(title=title)
            return redirect('core:chat_detail', conversation_id=conversation.id)
        
        # Send a message in an existing conversation
        elif 'text' in request.POST and conversation_id:
            text = request.POST.get('text').strip()
            if text:
                conversation = get_object_or_404(Conversation, id=conversation_id)
                Message.objects.create(conversation=conversation, content=text, sender='user')
                
                # Set is_generating to True
                conversation.is_generating = True
                conversation.save()

                # Start AI processing in a background thread
                thread = threading.Thread(target=run_ai_process_in_background, args=(conversation_id,))
                thread.daemon = True
                thread.start()
                
                return JsonResponse({'status': 'success', 'conversation_id': conversation_id})
            return JsonResponse({'status': 'error', 'message': 'Text is required.'}, status=400)

    conversations = Conversation.objects.order_by('-created_at')
    selected_conversation = None
    if conversation_id:
        selected_conversation = get_object_or_404(Conversation, id=conversation_id)

    return render(request, 'core/chat.html', {
        'conversation_list': conversations,
        'selected_conversation': selected_conversation,
        'timestamp': int(time.time()),
    })


def conversation_list(request):
    conversations = Conversation.objects.order_by('-created_at')
    return render(request, 'core/conversation_list.html', {'conversation_list': conversations})


def get_conversation_messages(request, conversation_id):
    conversation = get_object_or_404(Conversation, id=conversation_id)
    messages = conversation.messages.order_by('created_at').values('sender', 'content', 'created_at')
    return JsonResponse({
        'messages': list(messages),
        'is_generating': conversation.is_generating
    })


def settings_view(request):
    custom_instructions, _ = Setting.objects.get_or_create(
        key='custom_instructions',
        defaults={'value': ''}
    )

    if request.method == 'POST':
        custom_instructions.value = request.POST.get('custom_instructions', '')
        custom_instructions.save()
        return redirect('core:settings')

    return render(request, 'core/settings.html', {
        'custom_instructions': custom_instructions
    })


@require_POST
def delete_conversation(request, conversation_id):
    conversation = get_object_or_404(Conversation, id=conversation_id)
    conversation.delete()
    return redirect('core:chat')
