<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use App\Models\Conversation;
use Anthropic\Laravel\Facades\Anthropic;
use Symfony\Component\HttpFoundation\StreamedResponse;
use App\Services\TokenCalculatorService;
use App\Services\ImageIdGeneratorService;

class ClaudeAiController extends Controller
{
	// Danh sách các model fallback khi model chính bị overloaded
	private $fallbackModels = [
		'claude-sonnet-4-20250514',  // Model mới nhất
		'claude-3-5-sonnet-20241022',
		'claude-3-sonnet-20240229',
		'claude-3-haiku-20240307',
	];

	public function generateStructure(Request $request): JsonResponse
	{
		$request->validate([
			'title'           => 'required|string|max:255',
			'description'     => 'required|string|max:1000',
			'conversation_id' => 'nullable|exists:conversations,id',
		]);

		try {
			$conversation = $request->conversation_id
				? Conversation::find($request->conversation_id)
				: Conversation::create([
					'user_id' => Auth::id(),
					'type'    => 'article_generation',
					'step'    => 'step_1',
					'status'  => 'active',
				]);

			$conversation->addMessage('user', "Title: {$request->title}\nDescription: {$request->description}");
			$conversation->updateContext([
				'title'       => $request->title,
				'description' => $request->description,
			]);

			$prompt = $this->buildStructurePrompt($request->title, $request->description);

			// Thử gọi API với retry logic
			$response = $this->callAnthropicWithRetry([
				'max_tokens' => (int) config('anthropic.max_tokens.structure'),
				'messages'   => [
					['role' => 'user', 'content' => $prompt]
				]
			]);

			$aiResponse = $response['content'][0]['text'];
			$parsedData = $this->parseStructureResponse($aiResponse);

			$conversation->addMessage('assistant', $aiResponse);
			$conversation->updateAiResponse('structure', $parsedData);
			$conversation->update(['step' => 'step_2']);

			return response()->json([
				'success'         => true,
				'conversation_id' => $conversation->id,
				'data'            => $parsedData,
			]);

		} catch (\Exception $e) {
			Log::error('Claude AI Structure Generation Error: ' . $e->getMessage(), [
				'title' => $request->title,
				'description' => $request->description,
				'error' => $e->getTraceAsString()
			]);

			return response()->json([
				'success' => false,
				'message' => $this->getErrorMessage($e),
			], 500);
		}
	}

	public function modifyStructure(Request $request): JsonResponse
	{
		$request->validate([
			'conversation_id'      => 'required|exists:conversations,id',
			'modification_request' => 'required|string|max:500',
		]);

		try {
			$conversation = Conversation::findOrFail($request->conversation_id);
			$currentStructure = $conversation->ai_responses['structure'] ?? [];

			$conversation->addMessage('user', "Modification request: {$request->modification_request}");

			$prompt = $this->buildModificationPrompt($currentStructure, $request->modification_request);

			// Thử gọi API với retry logic
			$response = $this->callAnthropicWithRetry([
				'max_tokens' => (int) config('anthropic.max_tokens.structure'),
				'messages'   => $this->buildConversationHistory($conversation, $prompt)
			]);

			$aiResponse = $response['content'][0]['text'];
			$parsedData = $this->parseStructureResponse($aiResponse);

			$conversation->addMessage('assistant', $aiResponse);
			$conversation->updateAiResponse('structure', $parsedData);

			return response()->json([
				'success' => true,
				'data'    => $parsedData,
			]);

		} catch (\Exception $e) {
			Log::error('Claude AI Structure Modification Error: ' . $e->getMessage(), [
				'conversation_id' => $request->conversation_id,
				'modification_request' => $request->modification_request,
				'error' => $e->getTraceAsString()
			]);

			return response()->json([
				'success' => false,
				'message' => $this->getErrorMessage($e),
			], 500);
		}
	}

	public function writeArticle(Request $request): StreamedResponse
	{
		$request->validate([
			'conversation_id' => 'required|exists:conversations,id',
		]);

		return new StreamedResponse(function () use ($request) {
			try {
				$conversation = Conversation::findOrFail($request->conversation_id);
				$structure = $conversation->ai_responses['structure'] ?? [];
				$context = $conversation->context ?? [];

				$conversation->update(['step' => 'step_3']);

				$prompt = $this->buildWritingPrompt($context, $structure);
				$conversation->addMessage('user', 'Request to write full article based on structure');

				// Calculate appropriate max tokens based on word count
				$tokenCalculator = app(TokenCalculatorService::class);
				$wordCountConfig = config('anthropic.content_config.article_word_count');
				$wordCount = $tokenCalculator->parseWordCount($wordCountConfig);
				$maxTokens = $tokenCalculator->calculateMaxTokens($wordCount);

				// Try calling API with retry logic for streaming
				$stream = $this->callAnthropicStreamWithRetry([
					'max_tokens' => $maxTokens,
					'stream'     => true,
					'messages'   => $this->buildConversationHistory($conversation, $prompt)
				]);

				$fullContent = '';

				foreach ($stream as $chunk) {
					if (isset($chunk['delta']['text'])) {
						$text = $chunk['delta']['text'];

						// Remove markdown code block markers if present
						$text = preg_replace('/^```html\s*/', '', $text);
						$text = preg_replace('/^```\s*/', '', $text);

						$fullContent .= $text;

						echo "data: " . json_encode([
								'type' => 'content',
								'data' => $text,
							]) . "\n\n";

						if (ob_get_level()) {
							ob_flush();
						}
						flush();
					}
				}

				// Clean up the final content - remove any markdown artifacts
				$fullContent = preg_replace('/^```html\s*/', '', $fullContent);
				$fullContent = preg_replace('/```\s*$/', '', $fullContent);
				$fullContent = trim($fullContent);

				$conversation->addMessage('assistant', $fullContent);
				$conversation->updateAiResponse('article_content', $fullContent);

				echo "data: " . json_encode([
						'type' => 'done',
						'data' => ['conversation_id' => $conversation->id],
					]) . "\n\n";

			} catch (\Exception $e) {
				echo "data: " . json_encode([
						'type' => 'error',
						'data' => ['message' => $e->getMessage()],
					]) . "\n\n";
			}
		}, 200, [
			'Content-Type'  => 'text/event-stream',
			'Cache-Control' => 'no-cache',
			'Connection'    => 'keep-alive',
		]);
	}

	public function modifyArticle(Request $request): StreamedResponse
	{
		$request->validate([
			'conversation_id'      => 'required|exists:conversations,id',
			'modification_request' => 'required|string|max:500',
		]);

		return new StreamedResponse(function () use ($request) {
			try {
				$conversation = Conversation::findOrFail($request->conversation_id);
				$currentContent = $conversation->ai_responses['article_content'] ?? '';

				$conversation->addMessage('user', "Article modification request: {$request->modification_request}");

				$prompt = $this->buildArticleModificationPrompt($currentContent, $request->modification_request);

				// Calculate appropriate max tokens based on word count
				$tokenCalculator = app(TokenCalculatorService::class);
				$wordCountConfig = config('anthropic.content_config.article_word_count');
				$wordCount = $tokenCalculator->parseWordCount($wordCountConfig);
				$maxTokens = $tokenCalculator->calculateMaxTokens($wordCount);

				// Thử gọi API với retry logic cho streaming
				$stream = $this->callAnthropicStreamWithRetry([
					'max_tokens' => $maxTokens,
					'stream'     => true,
					'messages'   => $this->buildConversationHistory($conversation, $prompt)
				]);

				$fullContent = '';

				foreach ($stream as $chunk) {
					if (isset($chunk['delta']['text'])) {
						$text = $chunk['delta']['text'];
						$fullContent .= $text;

						echo "data: " . json_encode([
								'type' => 'content',
								'data' => $text,
							]) . "\n\n";

						if (ob_get_level()) {
							ob_flush();
						}
						flush();
					}
				}

				$conversation->addMessage('assistant', $fullContent);
				$conversation->updateAiResponse('article_content', $fullContent);

				echo "data: " . json_encode([
						'type' => 'done',
						'data' => ['conversation_id' => $conversation->id],
					]) . "\n\n";

			} catch (\Exception $e) {
				echo "data: " . json_encode([
						'type' => 'error',
						'data' => ['message' => $e->getMessage()],
					]) . "\n\n";
			}
		}, 200, [
			'Content-Type'  => 'text/event-stream',
			'Cache-Control' => 'no-cache',
			'Connection'    => 'keep-alive',
		]);
	}

	private function buildStructurePrompt(string $title, string $description): string
	{
		$seoTagsCount = config('anthropic.content_config.seo_tags_count');
		$structureSections = config('anthropic.content_config.structure_sections');
		
		// Parse structure sections (can be single number or range like "6:10")
		$sectionsText = $this->parseStructureSections($structureSections);
		
		return "あなたはテクノロジー分野の専門コンテンツライターです。以下のタイトルと説明を分析して、以下を作成してください：

1. {$seoTagsCount}個の主要SEOタグ（フォーカスキーワード）- 内容に適し、Googleで上位ランキングが期待できるもの
2. 記事の詳細構成 - {$sectionsText}個の主要セクションと各セクションの内容概要

タイトル: {$title}
説明: {$description}

以下のJSON形式で結果を返してください：
{
  \"seo_tags\": [\"tag1\", \"tag2\", \"tag3\", ...],
  \"structure\": [
    {
      \"section\": \"セクション1のタイトル\",
      \"content_outline\": \"このセクションに書く内容の簡潔な説明\"
    },
    {
      \"section\": \"セクション2のタイトル\",
      \"content_outline\": \"このセクションに書く内容の簡潔な説明\"
    }
  ]
}

注意：JSONのみを返し、他のテキストや説明は追加しないでください。";
	}

	private function buildModificationPrompt(array $currentStructure, string $modificationRequest): string
	{
		$structureJson = json_encode($currentStructure, JSON_UNESCAPED_UNICODE);

		return "現在の記事構成と修正要求に基づいて、新しい構成を作成してください：

現在の構成:
{$structureJson}

修正要求:
{$modificationRequest}

以前と同じJSON形式で修正された構成を返してください：
{
  \"seo_tags\": [...],
  \"structure\": [...]
}

注意：JSONのみを返し、他のテキストや説明は追加しないでください。";
	}

	private function buildWritingPrompt(array $context, array $structure): string
	{
		$title = $context['title'] ?? '';
		$description = $context['description'] ?? '';
		$structureJson = json_encode($structure, JSON_UNESCAPED_UNICODE);
		
		// Get dynamic word count configuration
		$tokenCalculator = app(TokenCalculatorService::class);
		$wordCountConfig = config('anthropic.content_config.article_word_count');
		$wordCount = $tokenCalculator->parseWordCount($wordCountConfig);
		
		// Generate unique featured image ID
		$imageIdGenerator = app(ImageIdGeneratorService::class);
		$conversationId = request()->get('conversation_id', 0);
		$featuredImageId = $imageIdGenerator->generateFeaturedImageId($title, $conversationId);

		return "以下の情報に基づいて、完全でプロフェッショナルな技術記事を執筆してください：

タイトル: {$title}
説明: {$description}
計画された構成: {$structureJson}

要件：
- 包括的で詳細な記事を書く（{$wordCount['text']}）
- **必ずHTML形式のみを使用** (Markdownは使用しない)
- **記事タイトル（<h1>タグ）は書かない** - 記事の最初から導入文で開始する
- 内容は専門的で価値の高いものにする
- 明確で魅力的な文体で書く
- 事前定義されたSEOタグを自然に統合する
- 明確な結論とコールトゥアクションを含める
- **各構成セクションに1つの画像を配置する**

重要な注意事項：
- **<h1>タグは絶対に使用しない** - タイトルは別途処理されます
- **Markdownフォーマット（#、##、###、*、**、```など）は絶対に使用しない**
- **HTMLタグのみを使用して記事を書く**
- **コードブロックマーカー（```html、```など）を含めない**
- **純粋なHTMLコンテンツのみを返す**

HTMLフォーマットガイドライン：
- **記事の最初に必ず以下のfeatured imageを含める：**
  <img id=\"{$featuredImageId}\" class=\"featured-img\" src=\"/storage/uploads/placeholder.png\" alt=\"Main illustration representing {$title}\" style=\"width: 100%; height: auto; margin: 20px 0; border-radius: 8px; display: none;\" />
- 主要な見出しには<h2>を使用
- サブ見出しには<h3>、<h4>を使用
- 段落には<p>を使用
- 箇条書きには<ul><li>を使用
- 番号付きリストには<ol><li>を使用
- 太字には<strong>を使用
- 斜体には<em>を使用
- 引用には<blockquote>を使用
- インラインコードには<code>を使用
- コードブロックには<pre><code>を使用

**イメージ配置ガイドライン：**
- **各構成セクション（<h2>）に1つの画像を配置**
- 「まとめ」「結論」「総括」セクションには画像を配置しない
- 各イメージには以下の形式を使用：
  <img id=\"img-{unique-id}\" src=\"/storage/uploads/placeholder.png\" alt=\"{セクション内容の詳細な画像説明を英語で}\" style=\"width: 100%; height: auto; margin: 20px 0; border-radius: 8px; display: none;\" />
- unique-idは8文字のランダム文字列を使用（例：abc12345）
- **alt属性には画像の詳細な説明を英語で記述**（画像生成AIが理解できる具体的な内容）
- イメージの説明は技術的で具体的に英語で記述（例：「Modern database design architecture diagram」「API endpoint structure visualization」「Detailed security workflow diagram」）
- **alt属性は必ず英語で記述し、日本語や他の言語は使用しない**

記事構成例：
<img id=\"{$featuredImageId}\" class=\"featured-img\" src=\"/storage/uploads/placeholder.png\" alt=\"Main conceptual diagram illustrating {$title}\" style=\"width: 100%; height: auto; margin: 20px 0; border-radius: 8px; display: none;\" />
<p>導入文...</p>
<h2>セクション1タイトル</h2>
<p>セクション1の内容...</p>
<img id=\"img-abc12345\" src=\"/storage/uploads/placeholder.png\" alt=\"Technical architecture diagram showing section 1 concepts\" style=\"width: 100%; height: auto; margin: 20px 0; border-radius: 8px; display: none;\" />
<h2>セクション2タイトル</h2>
<p>セクション2の内容...</p>
<img id=\"img-def67890\" src=\"/storage/uploads/placeholder.png\" alt=\"Implementation screenshot demonstrating section 2 features\" style=\"width: 100%; height: auto; margin: 20px 0; border-radius: 8px; display: none;\" />

**featured imageから開始してHTMLタグのみを使用して記事を書いてください：**";
	}

	private function buildArticleModificationPrompt(string $currentContent, string $modificationRequest): string
	{
		return "現在の記事と修正要求に基づいて、新しいバージョンを作成してください：

現在の記事:
{$currentContent}

修正要求:
{$modificationRequest}

TipTapエディタに対応したHTML形式で、修正された完全な記事を返してください。
執筆ガイドラインで指定された適切なHTMLタグを使用してください。";
	}

	private function buildConversationHistory(Conversation $conversation, string $currentPrompt): array
	{
		$messages = [];

		// Add previous messages for context
		foreach (($conversation->messages ?? []) as $message) {
			$messages[] = [
				'role'    => $message['role'],
				'content' => $message['content']
			];
		}

		// Add current prompt
		$messages[] = [
			'role'    => 'user',
			'content' => $currentPrompt
		];

		return $messages;
	}

	private function parseStructureResponse(string $response): array
	{
		// Try to extract JSON from the response
		preg_match('/\{.*\}/s', $response, $matches);

		if (empty($matches)) {
			return [
				'seo_tags'  => [],
				'structure' => []
			];
		}

		$decoded = json_decode($matches[0], true);

		return $decoded ?: [
			'seo_tags'  => [],
			'structure' => []
		];
	}

	/**
	 * Public method for external use (e.g., Jobs)
	 */
	public function callAnthropicAPI(array $params, int $maxRetries = 3)
	{
		return $this->callAnthropicWithRetry($params, $maxRetries);
	}

	/**
	 * Gọi Anthropic API với retry logic và fallback models
	 */
	private function callAnthropicWithRetry(array $params, int $maxRetries = 3)
	{
		$lastException = null;
		$currentModel = config('anthropic.model');
		$modelsToTry = array_unique(array_merge([$currentModel], $this->fallbackModels));

		foreach ($modelsToTry as $model) {
			for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
				try {
					Log::info("Attempting Claude API call", [
						'model' => $model,
						'attempt' => $attempt
					]);

					$response = Anthropic::messages()->create(array_merge($params, [
						'model' => $model
					]));

					Log::info("Claude API call successful", ['model' => $model]);
					return $response;

				} catch (\Exception $e) {
					$lastException = $e;
					$errorMessage = $e->getMessage();

					Log::warning("Claude API call failed", [
						'model' => $model,
						'attempt' => $attempt,
						'error' => $errorMessage
					]);

					// Nếu lỗi overloaded hoặc rate limit, đợi trước khi retry
					if (stripos($errorMessage, 'overloaded') !== false ||
					    stripos($errorMessage, 'rate') !== false ||
					    stripos($errorMessage, '529') !== false) {

						if ($attempt < $maxRetries) {
							// Exponential backoff: 2^attempt seconds
							$waitTime = pow(2, $attempt);
							Log::info("Waiting {$waitTime} seconds before retry...");
							sleep($waitTime);
							continue;
						}
					}

					// Nếu lỗi khác không phải overloaded, throw luôn
					if (stripos($errorMessage, 'overloaded') === false) {
						throw $e;
					}
				}
			}
		}

		// Nếu tất cả attempts và models đều thất bại
		throw $lastException ?: new \Exception('Failed to connect to Claude API after all retries');
	}

	/**
	 * Gọi Anthropic API với streaming và retry logic
	 */
	private function callAnthropicStreamWithRetry(array $params, int $maxRetries = 3)
	{
		$lastException = null;
		$currentModel = config('anthropic.model');
		$modelsToTry = array_unique(array_merge([$currentModel], $this->fallbackModels));

		foreach ($modelsToTry as $model) {
			for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
				try {
					Log::info("Attempting Claude API stream call", [
						'model' => $model,
						'attempt' => $attempt
					]);

					// Sử dụng createStreamed() thay vì create() với stream option
					// Loại bỏ 'stream' => true khỏi params vì createStreamed() tự động stream
					$streamParams = $params;
					unset($streamParams['stream']);

					$stream = Anthropic::messages()->createStreamed(array_merge($streamParams, [
						'model' => $model
					]));

					Log::info("Claude API stream call successful", ['model' => $model]);
					return $stream;

				} catch (\Exception $e) {
					$lastException = $e;
					$errorMessage = $e->getMessage();

					Log::warning("Claude API stream call failed", [
						'model' => $model,
						'attempt' => $attempt,
						'error' => $errorMessage
					]);

					// Nếu lỗi overloaded hoặc rate limit, đợi trước khi retry
					if (stripos($errorMessage, 'overloaded') !== false ||
					    stripos($errorMessage, 'rate') !== false ||
					    stripos($errorMessage, '529') !== false) {

						if ($attempt < $maxRetries) {
							// Exponential backoff
							$waitTime = pow(2, $attempt);
							Log::info("Waiting {$waitTime} seconds before retry...");
							sleep($waitTime);
							continue;
						}
					}

					// Nếu lỗi khác không phải overloaded, throw luôn
					if (stripos($errorMessage, 'overloaded') === false) {
						throw $e;
					}
				}
			}
		}

		// Nếu tất cả attempts và models đều thất bại
		throw $lastException ?: new \Exception('Failed to connect to Claude API stream after all retries');
	}

	/**
	 * Parse structure sections configuration
	 */
	private function parseStructureSections(string $config): string
	{
		// Handle range format like "6:10" or single number like "8"
		if (strpos($config, ':') !== false) {
			[$min, $max] = explode(':', $config);
			return "{$min}から{$max}";
		}
		
		return $config;
	}

	/**
	 * Tạo error message thân thiện cho user
	 */
	private function getErrorMessage(\Exception $e): string
	{
		$message = $e->getMessage();

		// Kiểm tra lỗi credit balance / billing
		if (stripos($message, 'credit balance') !== false || stripos($message, 'billing') !== false) {
			return 'Your Claude AI credit balance is too low. Please go to Anthropic Console > Plans & Billing to upgrade or purchase credits.';
		}

		// Kiểm tra lỗi quota exceeded
		if (stripos($message, 'quota') !== false || stripos($message, 'exceeded') !== false) {
			return 'API quota exceeded. You have reached your usage limit. Please check your Anthropic account or upgrade your plan.';
		}

		if (stripos($message, 'overloaded') !== false) {
			return 'The AI service is currently experiencing high demand. The system will automatically retry with alternative models. Please wait a moment...';
		}

		if (stripos($message, 'rate') !== false || stripos($message, '429') !== false) {
			return 'API rate limit reached. Please wait a moment and try again.';
		}

		if (stripos($message, 'api_key') !== false || stripos($message, 'authentication') !== false || stripos($message, '401') !== false) {
			return 'API authentication failed. Please check your API key configuration in the .env file.';
		}

		if (stripos($message, 'model') !== false || stripos($message, '404') !== false) {
			return 'The specified AI model is not available. Please check the model name or try alternative models.';
		}

		// Kiểm tra lỗi network/connection
		if (stripos($message, 'connection') !== false || stripos($message, 'network') !== false || stripos($message, 'timeout') !== false) {
			return 'Network connection error. Please check your internet connection and try again.';
		}

		// Kiểm tra lỗi invalid request
		if (stripos($message, 'invalid') !== false || stripos($message, '400') !== false) {
			return 'Invalid request to Claude AI. Please check your input and try again.';
		}

		// Nếu là lỗi khác, trả về message gốc từ API nếu nó đủ ngắn và có ý nghĩa
		if (strlen($message) < 200 && !strpos($message, 'Exception') && !strpos($message, 'Stack trace')) {
			Log::warning('Claude API error shown to user: ' . $message);
			return $message;
		}

		// Log chi tiết lỗi nhưng trả về message chung cho user
		Log::error('Unexpected Claude API error: ' . $message);
		return 'An unexpected error occurred while generating content. Please try again or contact support if the issue persists.';
	}
}
