import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ListToolsRequestSchema,
  ListResourcesRequestSchema,
  ReadResourceRequestSchema,
  ErrorCode,
  McpError,
} from '@modelcontextprotocol/sdk/types.js';
import { QuickBooksClient } from './clients/quickbooks.js';

export interface MCPServerConfig {
  name: string;
  version: string;
  qboClient: QuickBooksClient;
}

/**
 * Tool module definition for lazy loading
 */
interface ToolModule {
  name: string;
  description: string;
  inputSchema: any;
  handler: (client: QuickBooksClient, args: any) => Promise<any>;
}

/**
 * Create MCP server for QuickBooks Online
 */
export function createMCPServer(config: MCPServerConfig): Server {
  const server = new Server(
    {
      name: config.name,
      version: config.version,
    },
    {
      capabilities: {
        tools: {},
        resources: {},
      },
    }
  );

  // Tool categories for lazy loading
  const toolCategories = [
    'invoices',
    'customers',
    'payments',
    'estimates',
    'bills',
    'vendors',
    'items',
    'accounts',
    'reports',
    'employees',
    'time-activities',
    'taxes',
    'purchases',
    'journal-entries',
  ];

  // Tool registry - will be populated lazily
  const toolRegistry = new Map<string, ToolModule>();

  /**
   * Register a tool module
   */
  function registerTool(tool: ToolModule): void {
    toolRegistry.set(tool.name, tool);
  }

  /**
   * Lazy load tool modules
   */
  async function loadToolModules(): Promise<void> {
    // Tool modules will be created later - for now we register placeholders
    // to demonstrate the lazy-loading pattern
    
    // Example tool structure - actual implementations will be in separate files
    registerTool({
      name: 'qbo_query',
      description: 'Execute SQL-like query against QuickBooks Online',
      inputSchema: {
        type: 'object',
        properties: {
          sql: {
            type: 'string',
            description: 'SQL-like query (e.g., "SELECT * FROM Invoice WHERE TotalAmt > 1000")',
          },
          startPosition: {
            type: 'number',
            description: 'Start position for pagination (1-indexed)',
            default: 1,
          },
          maxResults: {
            type: 'number',
            description: 'Maximum results to return (max 1000)',
            default: 100,
          },
        },
        required: ['sql'],
      },
      handler: async (client, args) => {
        return await client.query(args.sql, {
          startPosition: args.startPosition,
          maxResults: args.maxResults,
        });
      },
    });

    registerTool({
      name: 'qbo_get_company_info',
      description: 'Get company information',
      inputSchema: {
        type: 'object',
        properties: {},
      },
      handler: async (client, _args) => {
        return await client.getCompanyInfo();
      },
    });

    registerTool({
      name: 'qbo_get_preferences',
      description: 'Get company preferences',
      inputSchema: {
        type: 'object',
        properties: {},
      },
      handler: async (client, _args) => {
        return await client.getPreferences();
      },
    });

    // Placeholder for future tool modules
    // These will be implemented in separate files like:
    // - src/tools/invoices.ts
    // - src/tools/customers.ts
    // - etc.
  }

  // List available tools
  server.setRequestHandler(ListToolsRequestSchema, async () => {
    await loadToolModules();
    
    return {
      tools: Array.from(toolRegistry.values()).map(tool => ({
        name: tool.name,
        description: tool.description,
        inputSchema: tool.inputSchema,
      })),
    };
  });

  // Handle tool calls
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
    await loadToolModules();
    
    const tool = toolRegistry.get(request.params.name);
    
    if (!tool) {
      throw new McpError(
        ErrorCode.MethodNotFound,
        `Unknown tool: ${request.params.name}`
      );
    }

    try {
      const result = await tool.handler(config.qboClient, request.params.arguments || {});
      
      return {
        content: [
          {
            type: 'text',
            text: JSON.stringify(result, null, 2),
          },
        ],
      };
    } catch (error: any) {
      // Structured error response
      const errorMessage = error.response?.data?.Fault?.Error?.[0]?.Message || error.message;
      const errorDetail = error.response?.data?.Fault?.Error?.[0]?.Detail || '';
      const errorCode = error.response?.data?.Fault?.Error?.[0]?.code || 'UNKNOWN';

      throw new McpError(
        ErrorCode.InternalError,
        `QuickBooks API error (${errorCode}): ${errorMessage}${errorDetail ? ` - ${errorDetail}` : ''}`
      );
    }
  });

  // Resource handlers for UI apps
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
    return {
      resources: [
        {
          uri: 'qbo://company/info',
          name: 'Company Information',
          description: 'Current company information',
          mimeType: 'application/json',
        },
        {
          uri: 'qbo://company/preferences',
          name: 'Company Preferences',
          description: 'Company preferences and settings',
          mimeType: 'application/json',
        },
        {
          uri: 'qbo://invoices/recent',
          name: 'Recent Invoices',
          description: 'Recent invoices (last 30 days)',
          mimeType: 'application/json',
        },
        {
          uri: 'qbo://customers/list',
          name: 'Customer List',
          description: 'All active customers',
          mimeType: 'application/json',
        },
      ],
    };
  });

  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
    const uri = request.params.uri;

    try {
      let data: any;

      if (uri === 'qbo://company/info') {
        data = await config.qboClient.getCompanyInfo();
      } else if (uri === 'qbo://company/preferences') {
        data = await config.qboClient.getPreferences();
      } else if (uri === 'qbo://invoices/recent') {
        const thirtyDaysAgo = new Date();
        thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
        const dateStr = thirtyDaysAgo.toISOString().split('T')[0];
        data = await config.qboClient.query(
          `SELECT * FROM Invoice WHERE TxnDate >= '${dateStr}'`,
          { maxResults: 100 }
        );
      } else if (uri === 'qbo://customers/list') {
        data = await config.qboClient.query(
          'SELECT * FROM Customer WHERE Active = true',
          { maxResults: 1000 }
        );
      } else {
        throw new McpError(
          ErrorCode.InvalidRequest,
          `Unknown resource: ${uri}`
        );
      }

      return {
        contents: [
          {
            uri,
            mimeType: 'application/json',
            text: JSON.stringify(data, null, 2),
          },
        ],
      };
    } catch (error: any) {
      throw new McpError(
        ErrorCode.InternalError,
        `Failed to read resource: ${error.message}`
      );
    }
  });

  return server;
}

/**
 * Start the MCP server with stdio transport
 */
export async function startServer(server: Server): Promise<void> {
  const transport = new StdioServerTransport();
  await server.connect(transport);
}
