#!/usr/bin/env node

/**
 * Mosaic Tasks CLI
 * 
 * Command-line interface for Mosaic Stack's Tasks API
 * Supports create, list, get, update, and delete operations
 */

const https = require('https');
const http = require('http');
const { URL } = require('url');

// Configuration from environment
const MOSAIC_API_URL = process.env.MOSAIC_API_URL || 'http://localhost:3001';
const MOSAIC_WORKSPACE_ID = process.env.MOSAIC_WORKSPACE_ID;
const MOSAIC_API_TOKEN = process.env.MOSAIC_API_TOKEN;

// Valid enum values
const TASK_STATUSES = ['NOT_STARTED', 'IN_PROGRESS', 'PAUSED', 'COMPLETED', 'ARCHIVED'];
const TASK_PRIORITIES = ['LOW', 'MEDIUM', 'HIGH'];

/**
 * Make HTTP request to Mosaic API
 */
function apiRequest(method, path, body = null) {
  return new Promise((resolve, reject) => {
    if (!MOSAIC_API_TOKEN) {
      reject(new Error('MOSAIC_API_TOKEN environment variable is required'));
      return;
    }
    if (!MOSAIC_WORKSPACE_ID) {
      reject(new Error('MOSAIC_WORKSPACE_ID environment variable is required'));
      return;
    }

    const url = new URL(path, MOSAIC_API_URL);
    const isHttps = url.protocol === 'https:';
    const client = isHttps ? https : http;

    const options = {
      method,
      hostname: url.hostname,
      port: url.port || (isHttps ? 443 : 80),
      path: url.pathname + url.search,
      headers: {
        'Authorization': `Bearer ${MOSAIC_API_TOKEN}`,
        'X-Workspace-ID': MOSAIC_WORKSPACE_ID,
        'Content-Type': 'application/json',
      },
    };

    const req = client.request(options, (res) => {
      let data = '';
      
      res.on('data', (chunk) => {
        data += chunk;
      });

      res.on('end', () => {
        try {
          const parsed = data ? JSON.parse(data) : null;
          
          if (res.statusCode >= 200 && res.statusCode < 300) {
            resolve(parsed);
          } else {
            const error = new Error(
              parsed?.message || `HTTP ${res.statusCode}: ${res.statusMessage}`
            );
            error.statusCode = res.statusCode;
            error.response = parsed;
            reject(error);
          }
        } catch (err) {
          reject(new Error(`Failed to parse response: ${err.message}`));
        }
      });
    });

    req.on('error', (err) => {
      reject(new Error(`Request failed: ${err.message}`));
    });

    if (body) {
      req.write(JSON.stringify(body));
    }

    req.end();
  });
}

/**
 * Parse command-line arguments
 */
function parseArgs(argv) {
  const args = {};
  const positional = [];

  for (let i = 0; i < argv.length; i++) {
    const arg = argv[i];
    
    if (arg.startsWith('--')) {
      const key = arg.slice(2);
      const value = argv[i + 1];
      
      if (value && !value.startsWith('--')) {
        args[key] = value;
        i++; // Skip next arg since we consumed it
      } else {
        args[key] = true; // Flag without value
      }
    } else {
      positional.push(arg);
    }
  }

  return { args, positional };
}

/**
 * Build query string from filters
 */
function buildQueryString(filters) {
  const params = new URLSearchParams();
  
  for (const [key, value] of Object.entries(filters)) {
    if (value !== undefined && value !== null) {
      params.append(key, value);
    }
  }
  
  const qs = params.toString();
  return qs ? `?${qs}` : '';
}

/**
 * Create a new task
 */
async function createTask(args) {
  const body = {
    title: args.title,
  };

  if (args.description) body.description = args.description;
  if (args.status) {
    if (!TASK_STATUSES.includes(args.status)) {
      throw new Error(`Invalid status. Must be one of: ${TASK_STATUSES.join(', ')}`);
    }
    body.status = args.status;
  }
  if (args.priority) {
    if (!TASK_PRIORITIES.includes(args.priority)) {
      throw new Error(`Invalid priority. Must be one of: ${TASK_PRIORITIES.join(', ')}`);
    }
    body.priority = args.priority;
  }
  if (args.due) body.dueDate = args.due;
  if (args['assignee-id']) body.assigneeId = args['assignee-id'];
  if (args['project-id']) body.projectId = args['project-id'];
  if (args['parent-id']) body.parentId = args['parent-id'];
  if (args['sort-order']) body.sortOrder = parseInt(args['sort-order'], 10);

  if (!body.title) {
    throw new Error('--title is required');
  }

  const result = await apiRequest('POST', '/api/tasks', body);
  console.log(JSON.stringify(result, null, 2));
  return result;
}

/**
 * List tasks with optional filters
 */
async function listTasks(args) {
  const filters = {};

  if (args.status) {
    if (!TASK_STATUSES.includes(args.status)) {
      throw new Error(`Invalid status. Must be one of: ${TASK_STATUSES.join(', ')}`);
    }
    filters.status = args.status;
  }
  if (args.priority) {
    if (!TASK_PRIORITIES.includes(args.priority)) {
      throw new Error(`Invalid priority. Must be one of: ${TASK_PRIORITIES.join(', ')}`);
    }
    filters.priority = args.priority;
  }
  if (args['assignee-id']) filters.assigneeId = args['assignee-id'];
  if (args['project-id']) filters.projectId = args['project-id'];
  if (args['parent-id']) filters.parentId = args['parent-id'];
  if (args.page) filters.page = args.page;
  if (args.limit) filters.limit = args.limit;

  // Handle overdue filter
  if (args.overdue) {
    filters.dueDateTo = new Date().toISOString();
    // Exclude completed and archived
    if (!filters.status) {
      console.error('Note: Overdue filter shows tasks with due date < now, excluding COMPLETED and ARCHIVED');
    }
  }

  if (args['due-from']) filters.dueDateFrom = args['due-from'];
  if (args['due-to']) filters.dueDateTo = args['due-to'];

  const query = buildQueryString(filters);
  const result = await apiRequest('GET', `/api/tasks${query}`);
  
  // Filter out completed/archived for overdue query
  if (args.overdue && result.data) {
    result.data = result.data.filter(
      task => task.status !== 'COMPLETED' && task.status !== 'ARCHIVED'
    );
    if (result.pagination) {
      result.pagination.total = result.data.length;
    }
  }

  console.log(JSON.stringify(result, null, 2));
  return result;
}

/**
 * Get a single task by ID
 */
async function getTask(taskId) {
  if (!taskId) {
    throw new Error('Task ID is required');
  }

  const result = await apiRequest('GET', `/api/tasks/${taskId}`);
  console.log(JSON.stringify(result, null, 2));
  return result;
}

/**
 * Update a task
 */
async function updateTask(taskId, args) {
  if (!taskId) {
    throw new Error('Task ID is required');
  }

  const body = {};

  if (args.title) body.title = args.title;
  if (args.description !== undefined) body.description = args.description;
  if (args.status) {
    if (!TASK_STATUSES.includes(args.status)) {
      throw new Error(`Invalid status. Must be one of: ${TASK_STATUSES.join(', ')}`);
    }
    body.status = args.status;
  }
  if (args.priority) {
    if (!TASK_PRIORITIES.includes(args.priority)) {
      throw new Error(`Invalid priority. Must be one of: ${TASK_PRIORITIES.join(', ')}`);
    }
    body.priority = args.priority;
  }
  if (args.due !== undefined) body.dueDate = args.due || null;
  if (args['assignee-id'] !== undefined) body.assigneeId = args['assignee-id'] || null;
  if (args['project-id'] !== undefined) body.projectId = args['project-id'] || null;
  if (args['parent-id'] !== undefined) body.parentId = args['parent-id'] || null;
  if (args['sort-order']) body.sortOrder = parseInt(args['sort-order'], 10);

  if (Object.keys(body).length === 0) {
    throw new Error('At least one field to update is required');
  }

  const result = await apiRequest('PATCH', `/api/tasks/${taskId}`, body);
  console.log(JSON.stringify(result, null, 2));
  return result;
}

/**
 * Delete a task
 */
async function deleteTask(taskId) {
  if (!taskId) {
    throw new Error('Task ID is required');
  }

  const result = await apiRequest('DELETE', `/api/tasks/${taskId}`);
  console.log(JSON.stringify(result || { success: true }, null, 2));
  return result;
}

/**
 * Show usage help
 */
function showHelp() {
  console.log(`
Mosaic Tasks CLI

Usage:
  node tasks.cjs <command> [options]

Commands:
  create          Create a new task
  list            List tasks with optional filters
  get <id>        Get a specific task by ID
  update <id>     Update a task
  delete <id>     Delete a task
  help            Show this help message

Create Options:
  --title <text>              Task title (required)
  --description <text>        Task description
  --status <status>           Status: ${TASK_STATUSES.join(', ')}
  --priority <priority>       Priority: ${TASK_PRIORITIES.join(', ')}
  --due <iso-date>            Due date (ISO 8601 format)
  --assignee-id <uuid>        Assign to user ID
  --project-id <uuid>         Link to project ID
  --parent-id <uuid>          Parent task ID (for subtasks)
  --sort-order <number>       Sort order (integer)

List Options:
  --status <status>           Filter by status
  --priority <priority>       Filter by priority
  --assignee-id <uuid>        Filter by assignee
  --project-id <uuid>         Filter by project
  --parent-id <uuid>          Filter by parent task
  --due-from <iso-date>       Filter tasks due after this date
  --due-to <iso-date>         Filter tasks due before this date
  --overdue                   Show only overdue tasks
  --page <number>             Page number (default: 1)
  --limit <number>            Results per page (default: 50, max: 100)

Update Options:
  --title <text>              Update title
  --description <text>        Update description
  --status <status>           Update status
  --priority <priority>       Update priority
  --due <iso-date>            Update due date (empty string to clear)
  --assignee-id <uuid>        Update assignee (empty string to clear)
  --project-id <uuid>         Update project (empty string to clear)
  --parent-id <uuid>          Update parent (empty string to clear)
  --sort-order <number>       Update sort order

Environment Variables:
  MOSAIC_API_URL              API base URL (default: http://localhost:3001)
  MOSAIC_WORKSPACE_ID         Workspace ID (required)
  MOSAIC_API_TOKEN            Authentication token (required)

Examples:
  # Create a task
  node tasks.cjs create --title "Review PR #123" --priority HIGH

  # List high priority tasks
  node tasks.cjs list --priority HIGH

  # List overdue tasks
  node tasks.cjs list --overdue

  # Get task details
  node tasks.cjs get abc-123-def-456

  # Mark task as completed
  node tasks.cjs update abc-123-def-456 --status COMPLETED

  # Delete a task
  node tasks.cjs delete abc-123-def-456
`);
}

/**
 * Main entry point
 */
async function main() {
  const { args, positional } = parseArgs(process.argv.slice(2));
  const command = positional[0];

  try {
    switch (command) {
      case 'create':
        await createTask(args);
        break;
      
      case 'list':
        await listTasks(args);
        break;
      
      case 'get':
        await getTask(positional[1]);
        break;
      
      case 'update':
        await updateTask(positional[1], args);
        break;
      
      case 'delete':
        await deleteTask(positional[1]);
        break;
      
      case 'help':
      case undefined:
        showHelp();
        break;
      
      default:
        console.error(`Unknown command: ${command}`);
        console.error('Run "node tasks.cjs help" for usage information');
        process.exit(1);
    }
  } catch (error) {
    console.error('Error:', error.message);
    
    if (error.statusCode === 401) {
      console.error('\nAuthentication failed. Check your MOSAIC_API_TOKEN.');
    } else if (error.statusCode === 403) {
      console.error('\nPermission denied. You may need higher workspace permissions.');
    } else if (error.statusCode === 404) {
      console.error('\nTask not found. Check the task ID.');
    } else if (error.response) {
      console.error('\nAPI Response:', JSON.stringify(error.response, null, 2));
    }
    
    process.exit(1);
  }
}

// Run if executed directly
if (require.main === module) {
  main();
}

module.exports = { createTask, listTasks, getTask, updateTask, deleteTask };
