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

interface ToolModule {
  name: string;
  description: string;
  inputSchema: {
    type: 'object';
    properties: Record<string, unknown>;
    required?: string[];
  };
  handler: (args: Record<string, unknown>, client: ShopifyClient) => Promise<unknown>;
}

interface ServerConfig {
  name: string;
  version: string;
  shopifyClient: ShopifyClient;
}

export class ShopifyMCPServer {
  private server: Server;
  private client: ShopifyClient;
  private toolModules: Map<string, () => Promise<ToolModule[]>> = new Map();

  constructor(config: ServerConfig) {
    this.client = config.shopifyClient;

    this.server = new Server(
      {
        name: config.name,
        version: config.version,
      },
      {
        capabilities: {
          tools: {},
          resources: {},
        },
      }
    );

    this.setupToolModules();
    this.setupHandlers();
  }

  /**
   * Register lazy-loaded tool modules
   */
  private setupToolModules(): void {
    // Tool modules will be dynamically imported when needed
    this.toolModules.set('products', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/products.js');
      return module.default;
    });

    this.toolModules.set('orders', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/orders.js');
      return module.default;
    });

    this.toolModules.set('customers', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/customers.js');
      return module.default;
    });

    this.toolModules.set('inventory', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/inventory.js');
      return module.default;
    });

    this.toolModules.set('collections', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/collections.js');
      return module.default;
    });

    this.toolModules.set('discounts', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/discounts.js');
      return module.default;
    });

    this.toolModules.set('shipping', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/shipping.js');
      return module.default;
    });

    this.toolModules.set('fulfillments', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/fulfillments.js');
      return module.default;
    });

    this.toolModules.set('themes', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/themes.js');
      return module.default;
    });

    this.toolModules.set('pages', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/pages.js');
      return module.default;
    });

    this.toolModules.set('blogs', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/blogs.js');
      return module.default;
    });

    this.toolModules.set('analytics', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/analytics.js');
      return module.default;
    });

    this.toolModules.set('webhooks', async () => {
      // @ts-ignore - Tool modules not created yet (foundation only)
      const module = await import('./tools/webhooks.js');
      return module.default;
    });
  }

  /**
   * Load all tool modules (called on list_tools)
   */
  private async loadAllTools(): Promise<ToolModule[]> {
    const allTools: ToolModule[] = [];

    // Only load modules that exist (we haven't created tool files yet)
    // This prevents errors during foundation setup
    for (const [moduleName, loader] of this.toolModules.entries()) {
      try {
        const tools = await loader();
        allTools.push(...tools);
      } catch (error) {
        // Tool module doesn't exist yet - this is expected during foundation setup
        console.log(`[Info] Tool module '${moduleName}' not loaded (not created yet)`);
      }
    }

    return allTools;
  }

  /**
   * Setup MCP protocol handlers
   */
  private setupHandlers(): void {
    // List available tools
    this.server.setRequestHandler(ListToolsRequestSchema, async () => {
      const timestamp = new Date().toISOString();
      console.log(`[${timestamp}] Listing tools`);

      const tools = await this.loadAllTools();

      return {
        tools: tools.map(tool => ({
          name: tool.name,
          description: tool.description,
          inputSchema: tool.inputSchema,
        })),
      };
    });

    // Handle tool calls
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      const timestamp = new Date().toISOString();
      console.log(`[${timestamp}] Tool call: ${request.params.name}`);

      try {
        const tools = await this.loadAllTools();
        const tool = tools.find(t => t.name === request.params.name);

        if (!tool) {
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify({
                  error: 'TOOL_NOT_FOUND',
                  message: `Tool '${request.params.name}' not found`,
                }),
              },
            ],
            isError: true,
          };
        }

        const result = await tool.handler(
          request.params.arguments as Record<string, unknown>,
          this.client
        );

        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify(result, null, 2),
            },
          ],
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        const errorCode = (error as Error & { code?: string }).code || 'UNKNOWN_ERROR';

        console.error(`[${timestamp}] Tool error:`, errorMessage);

        return {
          content: [
            {
              type: 'text',
              text: JSON.stringify({
                error: errorCode,
                message: errorMessage,
              }),
            },
          ],
          isError: true,
        };
      }
    });

    // List resources (for UI apps)
    this.server.setRequestHandler(ListResourcesRequestSchema, async () => {
      const timestamp = new Date().toISOString();
      console.log(`[${timestamp}] Listing resources`);

      return {
        resources: [
          {
            uri: 'shopify://products',
            name: 'Products',
            description: 'List all products in the store',
            mimeType: 'application/json',
          },
          {
            uri: 'shopify://orders',
            name: 'Orders',
            description: 'List all orders in the store',
            mimeType: 'application/json',
          },
          {
            uri: 'shopify://customers',
            name: 'Customers',
            description: 'List all customers in the store',
            mimeType: 'application/json',
          },
          {
            uri: 'shopify://inventory',
            name: 'Inventory',
            description: 'View inventory levels across locations',
            mimeType: 'application/json',
          },
        ],
      };
    });

    // Read resource (for UI apps)
    this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
      const timestamp = new Date().toISOString();
      console.log(`[${timestamp}] Reading resource: ${request.params.uri}`);

      const uri = request.params.uri;

      try {
        let data: unknown;

        if (uri === 'shopify://products') {
          data = await this.client.list('/products.json');
        } else if (uri === 'shopify://orders') {
          data = await this.client.list('/orders.json');
        } else if (uri === 'shopify://customers') {
          data = await this.client.list('/customers.json');
        } else if (uri === 'shopify://inventory') {
          data = await this.client.list('/inventory_levels.json');
        } else {
          throw new Error(`Unknown resource URI: ${uri}`);
        }

        return {
          contents: [
            {
              uri,
              mimeType: 'application/json',
              text: JSON.stringify(data, null, 2),
            },
          ],
        };
      } catch (error) {
        const errorMessage = error instanceof Error ? error.message : String(error);
        console.error(`[${timestamp}] Resource read error:`, errorMessage);

        return {
          contents: [
            {
              uri,
              mimeType: 'application/json',
              text: JSON.stringify({
                error: 'RESOURCE_READ_ERROR',
                message: errorMessage,
              }),
            },
          ],
        };
      }
    });
  }

  /**
   * Start the server with stdio transport
   */
  async start(): Promise<void> {
    const transport = new StdioServerTransport();
    await this.server.connect(transport);
    console.log('Shopify MCP server running');
  }

  /**
   * Get the underlying MCP server instance (for custom transports)
   */
  getServer(): Server {
    return this.server;
  }
}
