import https from 'node:https';
import type { LlmProvider, CompletionOptions, CompletionResult, ChatMessage, ToolCall } from './types.js';

export interface AnthropicConfig {
  apiKey: string;
  defaultModel?: string;
}

/**
 * Anthropic Claude provider using the Messages API.
 */
export class AnthropicProvider implements LlmProvider {
  readonly name = 'anthropic';
  private apiKey: string;
  private defaultModel: string;

  constructor(config: AnthropicConfig) {
    this.apiKey = config.apiKey;
    this.defaultModel = config.defaultModel ?? 'claude-sonnet-4-20250514';
  }

  async complete(options: CompletionOptions): Promise<CompletionResult> {
    const model = options.model ?? this.defaultModel;

    // Separate system message from conversation
    const systemMessages = options.messages.filter((m) => m.role === 'system');
    const conversationMessages = options.messages.filter((m) => m.role !== 'system');

    const body: Record<string, unknown> = {
      model,
      max_tokens: options.maxTokens ?? 4096,
      messages: conversationMessages.map(toAnthropicMessage),
    };

    if (systemMessages.length > 0) {
      body.system = systemMessages.map((m) => m.content).join('\n');
    }
    if (options.temperature !== undefined) body.temperature = options.temperature;

    if (options.tools && options.tools.length > 0) {
      body.tools = options.tools.map((t) => ({
        name: t.name,
        description: t.description,
        input_schema: t.inputSchema,
      }));
    }

    const response = await this.request(body);
    return parseAnthropicResponse(response);
  }

  async listModels(): Promise<string[]> {
    // Anthropic doesn't have a models listing endpoint; return known models
    return [
      'claude-opus-4-20250514',
      'claude-sonnet-4-20250514',
      'claude-sonnet-4-5-20250514',
      'claude-haiku-3-5-20241022',
    ];
  }

  async isAvailable(): Promise<boolean> {
    try {
      // Send a minimal request to check API key
      await this.complete({
        messages: [{ role: 'user', content: 'hi' }],
        maxTokens: 1,
      });
      return true;
    } catch {
      return false;
    }
  }

  private request(body: unknown): Promise<unknown> {
    return new Promise((resolve, reject) => {
      const payload = JSON.stringify(body);
      const isOAuth = this.apiKey.startsWith('sk-ant-oat');
      const opts = {
        hostname: 'api.anthropic.com',
        port: 443,
        path: '/v1/messages',
        method: 'POST',
        timeout: 120000,
        headers: {
          ...(isOAuth
            ? { 'Authorization': `Bearer ${this.apiKey}` }
            : { 'x-api-key': this.apiKey }),
          'anthropic-version': '2023-06-01',
          'Content-Type': 'application/json',
          'Content-Length': Buffer.byteLength(payload),
        },
      };

      const req = https.request(opts, (res) => {
        const chunks: Buffer[] = [];
        res.on('data', (chunk: Buffer) => chunks.push(chunk));
        res.on('end', () => {
          const raw = Buffer.concat(chunks).toString('utf-8');
          try {
            resolve(JSON.parse(raw));
          } catch {
            reject(new Error(`Invalid JSON response: ${raw.slice(0, 200)}`));
          }
        });
      });
      req.on('error', reject);
      req.on('timeout', () => {
        req.destroy();
        reject(new Error('Request timed out'));
      });
      req.write(payload);
      req.end();
    });
  }
}

function toAnthropicMessage(msg: ChatMessage): Record<string, unknown> {
  if (msg.role === 'tool') {
    return {
      role: 'user',
      content: [{
        type: 'tool_result',
        tool_use_id: msg.toolCallId,
        content: msg.content,
      }],
    };
  }
  return {
    role: msg.role === 'user' ? 'user' : 'assistant',
    content: msg.content,
  };
}

function parseAnthropicResponse(raw: unknown): CompletionResult {
  const data = raw as {
    content?: Array<{
      type: string;
      text?: string;
      id?: string;
      name?: string;
      input?: Record<string, unknown>;
    }>;
    stop_reason?: string;
    usage?: {
      input_tokens?: number;
      output_tokens?: number;
    };
  };

  let content = '';
  const toolCalls: ToolCall[] = [];

  for (const block of data.content ?? []) {
    if (block.type === 'text' && block.text) {
      content += block.text;
    } else if (block.type === 'tool_use' && block.id && block.name) {
      toolCalls.push({
        id: block.id,
        name: block.name,
        arguments: block.input ?? {},
      });
    }
  }

  const inputTokens = data.usage?.input_tokens ?? 0;
  const outputTokens = data.usage?.output_tokens ?? 0;

  const finishReason = data.stop_reason === 'tool_use' ? 'tool_calls' as const
    : data.stop_reason === 'max_tokens' ? 'length' as const
    : 'stop' as const;

  return {
    content,
    toolCalls,
    usage: {
      promptTokens: inputTokens,
      completionTokens: outputTokens,
      totalTokens: inputTokens + outputTokens,
    },
    finishReason,
  };
}
