<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Jobs\ProcessKeywordJob;
use App\Models\AiKeyword;
use App\Models\Category;
use App\Models\Tag;
use App\Services\ImageIdGeneratorService;
use Illuminate\Bus\Batch;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Str;
use Inertia\Inertia;
use Anthropic\Laravel\Facades\Anthropic;

class AiSupportController extends Controller
{
	public function dashboard()
	{
		return Inertia::render('admin/ai-support/Dashboard');
	}
	
	public function keywords(Request $request)
	{
		$query = AiKeyword::query();
		
		// Get pagination parameters
		$perPage = $request->get('per_page', 15);
		$status = $request->get('status', '');
		$search = $request->get('search');
		
		// Status filter
		if ($status && $status !== 'all') {
			$query->where('status', $status);
		}
		
		// Search functionality
		if ($search) {
			$query->where(function ($q) use ($search) {
				$q->where('keyword', 'LIKE', "%{$search}%")
					->orWhere('persona', 'LIKE', "%{$search}%");
			});
		}
		
		$keywords = $query
			->orderBy('updated_at', 'desc')
			->orderBy('status', 'desc')
			->paginate($perPage);
		
		// Available status options
		$statusOptions = ['processing', 'completed', 'failed'];
		
		return Inertia::render('admin/ai-support/Keywords', [
			'keywords'      => $keywords,
			'statusOptions' => $statusOptions,
			'filters'       => [
				'status'   => $status,
				'search'   => $search,
				'per_page' => $perPage,
			],
		]);
	}
	
	/**
	 * Get keywords for AI Writer (API endpoint)
	 */
	public function getKeywordsForWriter(Request $request)
	{
		$query = AiKeyword::query();

		// Allow all keywords regardless of status (removed 'completed' filter)
		// Users can select any keyword to write articles

		// Search functionality
		if ($request->has('search') && $request->search) {
			$search = $request->search;
			$query->where(function ($q) use ($search) {
				$q->where('keyword', 'LIKE', "%{$search}%")
					->orWhere('intent', 'LIKE', "%{$search}%")
					->orWhere('persona', 'LIKE', "%{$search}%");
			});
		}
		
		// Get pagination parameters
		$limit = $request->get('limit', 50);
		$page = $request->get('page', 1);
		$offset = ($page - 1) * $limit;

		// Get total count before pagination
		$total = $query->count();

		// Order by priority (high -> medium -> low) then by volume desc
		$keywords = $query
			->orderByRaw("FIELD(priority, 'high', 'medium', 'low') ASC")
			->orderBy('volume', 'desc')
			->orderBy('updated_at', 'desc')
			->limit($limit)
			->offset($offset)
			->get();

		return response()->json([
			'success' => true,
			'data'    => $keywords,
			'total'   => $keywords->count(),
			'pagination' => [
				'current_page' => (int) $page,
				'per_page' => (int) $limit,
				'total' => $total,
				'last_page' => ceil($total / $limit),
				'has_more' => $page < ceil($total / $limit)
			],
		]);
	}
	
	public function csvUpload()
	{
		return Inertia::render('admin/ai-support/CsvUpload');
	}
	
	public function aiWriter()
	{
		return Inertia::render('admin/ai-support/AiWriter');
	}
	
	public function aiArticles()
	{
		return Inertia::render('admin/ai-support/AiArticles');
	}
	
	public function uploadFile(Request $request)
	{
		try {
			$request->validate([
				'file' => 'required|file|mimes:csv,txt|max:2048',
			]);
			
			$file = $request->file('file');
			
			// Save CSV file for backup with timestamp
			$timestamp = now()->format('Ymd_His');
			$originalName = pathinfo($file->getClientOriginalName(), PATHINFO_FILENAME);
			$backupFileName = $originalName . '_' . $timestamp . '.csv';
			
			// Store file in public/uploads/csv directory
			$backupPath = $file->storeAs('uploads/csv', $backupFileName, 'public');
			
			// Get file info for processing
			$csvData = array_map('str_getcsv', file($file->getRealPath()));
			
			// Remove header row if exists (check if first row looks like header)
			$firstRow = $csvData[0] ?? [];
			if (!empty($firstRow[0]) && (strtolower(trim($firstRow[0])) === 'keyword' || strtolower(trim($firstRow[0])) === 'keywords')) {
				array_shift($csvData);
			}
			
			// Count valid rows
			$validRows = 0;
			foreach ($csvData as $row) {
				if (!empty($row[0]) && !empty(trim($row[0]))) {
					$validRows++;
				}
			}
			
			// Validate if CSV has any keywords
			if ($validRows === 0) {
				return response()->json([
					'success' => false,
					'message' => 'CSV file is empty or contains no valid keywords. Please check your file and try again.',
				], 422);
			}
			
			return response()->json([
				'success' => true,
				'message' => 'File uploaded successfully! Ready for processing.',
				'data'    => [
					'filename'    => $backupFileName,
					'path'        => $backupPath,
					'total_rows'  => $validRows,
					'uploaded_at' => now()->toISOString(),
				],
			]);
			
		} catch (\Exception $e) {
			return response()->json([
				'success' => false,
				'message' => $e->getMessage(),
			], 422);
		}
	}
	
	public function processCSV(Request $request)
	{
		try {
			$request->validate([
				'filename' => 'required|string',
				'path'     => 'required|string',
			]);
			
			$filePath = storage_path('app/public/' . $request->path);
			
			if (!file_exists($filePath)) {
				return response()->json([
					'success' => false,
					'message' => 'CSV file not found. Please upload the file again.',
				], 404);
			}
			
			// Process CSV data
			$csvData = array_map('str_getcsv', file($filePath));
			
			// Remove header row if exists (check if first row looks like header)
			$firstRow = $csvData[0] ?? [];
			if (!empty($firstRow[0]) && (strtolower(trim($firstRow[0])) === 'keyword' || strtolower(trim($firstRow[0])) === 'keywords')) {
				array_shift($csvData);
			}
			
			// Extract and normalize all keywords from CSV
			$csvKeywords = [];
			
			foreach ($csvData as $rowIndex => $row) {
				if (empty($row[0])) {
					continue; // Skip empty rows
				}
				
				// Trim whitespace and normalize the keyword
				$keyword = trim($row[0]);
				
				// Skip if empty after trimming
				if (empty($keyword)) {
					continue; // Skip empty keywords
				}
				
				// Remove ALL whitespace from keyword (including spaces between words)
				$keyword = preg_replace('/\s+/', '', $keyword);
				
				// Automatically remove duplicates within CSV file (no counting needed)
				if (!in_array($keyword, $csvKeywords)) {
					$csvKeywords[] = $keyword;
				}
			}
			
			// Validate if CSV has any keywords after processing
			if (empty($csvKeywords)) {
				return response()->json([
					'success' => false,
					'message' => 'CSV file is empty or contains no valid keywords. Please check your file and try again.',
				], 422);
			}
			
			// Get all existing keywords from database in one query for better performance
			$existingKeywords = AiKeyword::whereIn('keyword', $csvKeywords)->pluck('keyword')->toArray();
			
			$jobs = [];
			
			// Create jobs for each keyword
			foreach ($csvKeywords as $keyword) {
				if (in_array($keyword, $existingKeywords)) {
					// Update existing keyword to reset status to processing for AI re-analysis
					AiKeyword::where('keyword', $keyword)->update(['status' => 'processing']);
				} else {
					// Create new keyword with processing status
					AiKeyword::create([
						'keyword'  => $keyword,
						'slug'     => urlencode($keyword), // $keyword already has no whitespace
						'volume'   => null,
						'intent'   => null,
						'persona'  => null,
						'priority' => null,
						'status'   => 'processing',
					]);
				}
				
				// Add job to process this keyword with AI
				$jobs[] = new ProcessKeywordJob($keyword);
			}
			
			// Dispatch batch jobs
			$batch = Bus::batch($jobs)
				->then(function (Batch $batch) {
					// All jobs completed successfully
				})
				->catch(function (Batch $batch, \Throwable $e) {
					// First batch job failure
				})
				->finally(function (Batch $batch) {
					// The batch has finished executing
				})
				->dispatch();
			
			return response()->json([
				'success' => true,
				'message' => 'Processing started! Keywords will be analyzed with AI.',
				'data'    => [
					'batch_id'   => $batch->id,
					'total_jobs' => count($jobs),
					'filename'   => $request->filename,
				],
			]);
			
		} catch (\Exception $e) {
			return response()->json([
				'success' => false,
				'message' => $e->getMessage(),
			], 422);
		}
	}
	
	public function checkBatchProgress(Request $request)
	{
		try {
			$request->validate([
				'batch_id' => 'required|string',
			]);
			
			$batch = Bus::findBatch($request->batch_id);
			
			if (!$batch) {
				return response()->json([
					'success' => false,
					'message' => 'Batch not found.',
				], 404);
			}
			
			return response()->json([
				'success' => true,
				'data'    => [
					'id'             => $batch->id,
					'total_jobs'     => $batch->totalJobs,
					'pending_jobs'   => $batch->pendingJobs,
					'processed_jobs' => $batch->processedJobs(),
					'progress'       => $batch->progress(),
					'finished'       => $batch->finished(),
					'cancelled'      => $batch->cancelled(),
					'failed_jobs'    => $batch->failedJobs,
					'created_at'     => $batch->createdAt,
					'finished_at'    => $batch->finishedAt,
				],
			]);
			
		} catch (\Exception $e) {
			return response()->json([
				'success' => false,
				'message' => $e->getMessage(),
			], 422);
		}
	}
	
	public function createKeyword()
	{
		return Inertia::render('admin/ai-support/CreateKeyword');
	}
	
	public function storeKeyword(Request $request)
	{
		$request->validate([
			'keyword'  => [
				'required',
				'string',
				'min:2',
				'max:255',
				function ($attribute, $value, $fail) {
					// Remove whitespace and check uniqueness
					$cleanKeyword = preg_replace('/\s+/', '', trim($value));
					if (AiKeyword::where('keyword', $cleanKeyword)->exists()) {
						$fail('The keyword already exists (after removing whitespace).');
					}
				}
			],
			'volume'   => 'nullable|integer|min:0',
			'intent'   => 'nullable|string|max:255',
			'persona'  => 'nullable|string|max:255',
			'priority' => 'nullable|in:high,medium,low',
		]);
		
		// Clean and process keyword
		$cleanKeyword = preg_replace('/\s+/', '', trim($request->keyword));
		
		try {
			AiKeyword::create([
				'keyword'  => $cleanKeyword,
				'volume'   => $request->volume,
				'intent'   => $request->intent,
				'persona'  => $request->persona,
				'priority' => $request->priority,
				'status'   => 'processing',
			]);
			
			return redirect()->route('admin.ai-support.keywords')
				->with('success', 'Keyword created successfully!');
			
		} catch (\Exception $e) {
			return back()->withErrors(['keyword' => 'Failed to create keyword: ' . $e->getMessage()]);
		}
	}
	
	public function editKeyword(AiKeyword $keyword)
	{
		return Inertia::render('admin/ai-support/EditKeyword', [
			'keyword' => $keyword,
		]);
	}
	
	public function updateKeyword(Request $request, AiKeyword $keyword)
	{
		$request->validate([
			'keyword'  => [
				'required',
				'string',
				'min:2',
				'max:255',
				function ($attribute, $value, $fail) use ($keyword) {
					// Remove whitespace and check uniqueness (except current)
					$cleanKeyword = preg_replace('/\s+/', '', trim($value));
					if (AiKeyword::where('keyword', $cleanKeyword)->where('id', '!=', $keyword->id)->exists()) {
						$fail('The keyword already exists (after removing whitespace).');
					}
				}
			],
			'volume'   => 'nullable|integer|min:0',
			'intent'   => 'nullable|string|max:255',
			'persona'  => 'nullable|string|max:255',
			'priority' => 'nullable|in:high,medium,low',
			'status'   => 'required|in:processing,completed,failed',
		]);
		
		// Clean and process keyword
		$cleanKeyword = preg_replace('/\s+/', '', trim($request->keyword));
		
		try {
			$keyword->update([
				'keyword'  => $cleanKeyword,
				'volume'   => $request->volume,
				'intent'   => $request->intent,
				'persona'  => $request->persona,
				'priority' => $request->priority,
				'status'   => $request->status,
			]);
			
			return redirect()->route('admin.ai-support.keywords')
				->with('success', 'Keyword updated successfully!');
			
		} catch (\Exception $e) {
			return back()->withErrors(['keyword' => 'Failed to update keyword: ' . $e->getMessage()]);
		}
	}
	
	public function destroyKeyword(AiKeyword $keyword)
	{
		try {
			$keyword->delete();
			return back()->with('success', 'Keyword deleted successfully!');
		} catch (\Exception $e) {
			return back()->with('error', 'Failed to delete keyword: ' . $e->getMessage());
		}
	}

	/**
	 * Re-analyze a keyword with AI
	 */
	public function reanalyzeKeyword($id)
	{
		try {
			// Find keyword by ID
			$keyword = AiKeyword::findOrFail($id);

			// Reset keyword status to processing
			$keyword->update([
				'status' => 'processing',
				'volume' => null,
				'intent' => null,
				'persona' => null,
				'priority' => null,
			]);

			// Dispatch job to re-analyze the keyword
			ProcessKeywordJob::dispatch($keyword->keyword);

			return response()->json([
				'success' => true,
				'message' => 'Keyword re-analysis started! It will be analyzed with AI shortly.',
			]);

		} catch (\Exception $e) {
			return response()->json([
				'success' => false,
				'message' => 'Failed to start re-analysis: ' . $e->getMessage(),
			], 500);
		}
	}

	/**
	 * Get keyword status for polling
	 */
	public function getKeywordStatus($id)
	{
		try {
			$keyword = AiKeyword::findOrFail($id);

			return response()->json([
				'success' => true,
				'data'    => [
					'id'       => $keyword->id,
					'keyword'  => $keyword->keyword,
					'status'   => $keyword->status,
					'volume'   => $keyword->volume,
					'intent'   => $keyword->intent,
					'persona'  => $keyword->persona,
					'priority' => $keyword->priority,
				],
			]);

		} catch (\Exception $e) {
			return response()->json([
				'success' => false,
				'message' => 'Keyword not found',
			], 404);
		}
	}

	public function bulkAction(Request $request)
	{
		$request->validate([
			'action'     => 'required|in:processing,completed,failed,delete',
			'keywords'   => 'required|array|min:1',
			'keywords.*' => 'exists:ai_keywords,id',
		]);
		
		try {
			$keywords = AiKeyword::whereIn('id', $request->keywords);
			
			if ($request->action === 'delete') {
				$count = $keywords->count();
				$keywords->delete();
				return back()->with('success', "{$count} keywords deleted successfully!");
			} else {
				$count = $keywords->update(['status' => $request->action]);
				return back()->with('success', "{$count} keywords updated to {$request->action} status!");
			}
		} catch (\Exception $e) {
			return back()->with('error', 'Bulk action failed: ' . $e->getMessage());
		}
	}
	
	/**
	 * Generate brief (article information) for AI Writer (API endpoint)
	 */
	public function generateBrief(Request $request)
	{
		try {
			$request->validate([
				'keyword'          => 'required|array',
				'keyword.id'       => 'required|integer',
				'keyword.keyword'  => 'required|string',
				'keyword.volume'   => 'nullable|integer',
				'keyword.intent'   => 'nullable|string',
				'keyword.persona'  => 'nullable|string',
				'keyword.priority' => 'nullable|string',
			]);
			
			$keyword = $request->input('keyword');
			$keywordText = $keyword['keyword'];
			$intent = $keyword['intent'] ?? 'Informational';
			$persona = $keyword['persona'] ?? 'ビジネスパーソン';
			$volume = $keyword['volume'] ?? 1000;
			
			// Build prompt for AI to generate article brief
			$prompt = $this->buildBriefPrompt($keywordText, $intent, $persona, $volume);
			
			// Call Claude AI to generate brief
			$response = Anthropic::messages()->create([
				'model'       => config('anthropic.model', 'claude-3-sonnet-20240229'),
				'max_tokens'  => (int)config('anthropic.max_tokens.structure', 3000),
				'messages'    => [
					[
						'role'    => 'user',
						'content' => $prompt
					]
				],
				'temperature' => 0.5  // Reduced for more consistent output
			]);
			
			// Parse AI response
			$aiContent = $response->content[0]->text ?? '';
			$aiContent = preg_replace('/^```json\s*/i', '', $aiContent);
			$aiContent = preg_replace('/\s*```$/i', '', $aiContent);
			$aiContent = trim($aiContent);
			
			// Parse JSON response from AI
			$briefData = json_decode($aiContent, true);
			
			if (!$briefData) {
				Log::error('Failed to parse AI response', ['raw_content' => $aiContent]);
				throw new \Exception('Invalid AI response format');
			}
			
			return response()->json([
				'success' => true,
				'message' => '記事情報が生成されました',
				'data'    => $briefData,
			]);
			
		} catch (\Exception $e) {
			Log::error('Brief Generation Failed', [
				'error'   => $e->getMessage(),
				'keyword' => $request->input('keyword.keyword', 'N/A')
			]);
			
			return response()->json([
				'success' => false,
				'message' => '記事情報生成に失敗しました: ' . $e->getMessage(),
			], 422);
		}
	}
	
	/**
	 * Build AI prompt for brief generation
	 */
	private function buildBriefPrompt(string $keyword, string $intent, string $persona, int $volume): string
	{
		$intentJapanese = [
			'Commercial'    => '商業的・購買検討',
			'Informational' => '情報収集',
			'Navigational'  => 'ナビゲーション',
			'Transactional' => '取引・購入'
		];
		
		$intentType = $intentJapanese[$intent] ?? '情報収集';
		
		return "あなたはSEOとコンテンツマーケティングの専門家です。以下のキーワードに基づいて、高品質な記事作成のための詳細な記事情報（ブリーフ）を生成してください。

【対象キーワード情報】
- キーワード: {$keyword}
- 検索ボリューム: {$volume}
- 検索意図: {$intentType}
- ターゲットペルソナ: {$persona}

【要求事項】
以下の項目を含む詳細な記事情報を生成してください：

1. キーワード分析
   - メインキーワード
   - サブキーワード（3-5個）
   - 関連キーワード（2-3個）
   - 共起語（3-5個）
   - LSIキーワード（3-5個）

2. 記事設計
   - 記事タイトル（30-35文字、SEO最適化）
   - テーマ（記事の中心テーマ）
   - 内容の要約（100-150文字）
   - メタディスクリプション（120-160文字、日本語SEO最適化）
   - 推奨文字数
   - 推奨セクション数

3. ターゲット設定
   - 具体的なペルソナ像（年齢、職業、課題など）
   - 検索意図の詳細

4. 価値設計
   - 記事が提供する価値
   - 具体的な価値提案
   - 実例・事例の提案

5. よくある質問（FAQ）
   - 読者が持つであろう3つの疑問と回答

以下のJSON形式で結果を返してください：
{
  \"mainKeyword\": \"メインキーワード\",
  \"subKeywords\": \"サブ1, サブ2, サブ3, サブ4, サブ5\",
  \"relatedKeywords\": \"関連1, 関連2, 関連3\",
  \"coOccurrence\": \"共起語1, 共起語2, 共起語3, 共起語4\",
  \"lsiKeywords\": \"LSI1, LSI2, LSI3, LSI4\",
  \"articleTitle\": \"SEO最適化されたタイトル\",
  \"theme\": \"記事のテーマ\",
  \"contentSummary\": \"記事内容の要約\",
  \"metaDescription\": \"120-160文字の日本語SEO最適化メタディスクリプション\",
  \"wordCount\": \"3000\",
  \"sectionCount\": \"6\",
  \"targetPersona\": \"詳細なペルソナ説明\",
  \"searchIntent\": \"{$intent}\",
  \"articleValue\": \"記事が提供する価値\",
  \"valueProposition\": \"具体的な価値提案\",
  \"examples\": \"実例・事例の説明\",
  \"faqs\": [
    {
      \"question\": \"質問1\",
      \"answer\": \"回答1\"
    },
    {
      \"question\": \"質問2\",
      \"answer\": \"回答2\"
    },
    {
      \"question\": \"質問3\",
      \"answer\": \"回答3\"
    }
  ]
}

注意：JSONのみを返し、他のテキストや説明は追加しないでください。";
	}
	
	/**
	 * Generate outline for AI Writer (API endpoint)
	 */
	public function generateOutline(Request $request)
	{
		try {
			$request->validate([
				'keyword'                  => 'required|array',
				'keyword.id'               => 'required|integer',
				'keyword.keyword'          => 'required|string',
				'briefData'                => 'required|array',
				'briefData.articleTitle'   => 'required|string',
				'briefData.wordCount'      => 'required|string',
				'briefData.sectionCount'   => 'required|string',
				'briefData.mainKeyword'    => 'required|string',
				'briefData.subKeywords'    => 'required|string',
				'briefData.theme'          => 'required|string',
				'briefData.targetPersona'  => 'required|string',
				'briefData.searchIntent'   => 'required|string',
			]);
			
			// Get data from request
			$keyword = $request->input('keyword.keyword');
			$briefData = $request->input('briefData');
			$targetWords = (int)$briefData['wordCount'];
			$targetSections = (int)$briefData['sectionCount']; // Get sectionCount
			
			// Build AI prompt for outline generation based on collected data
			$prompt = $this->buildOutlinePrompt($keyword, $briefData, $targetWords, $targetSections);
			
			// Call Claude AI to generate outline
			$outlineData = $this->generateAIOutline($prompt, $keyword, $briefData, $targetWords);
			
			return response()->json([
				'success' => true,
				'message' => '構成が生成されました',
				'data'    => $outlineData,
			]);
			
		} catch (\Exception $e) {
			return response()->json([
				'success' => false,
				'message' => '構成生成に失敗しました: ' . $e->getMessage(),
			], 422);
		}
	}
	
	/**
	 * Build AI prompt for outline generation
	 */
	private function buildOutlinePrompt(string $keyword, array $briefData, int $targetWords, int $targetSections): string
	{
		$mainKeyword = $briefData['mainKeyword'];
		$subKeywords = $briefData['subKeywords'];
		$theme = $briefData['theme'];
		$targetPersona = $briefData['targetPersona'];
		$searchIntent = $briefData['searchIntent'];
		$articleTitle = $briefData['articleTitle'];
		
		return "あなたはSEOとコンテンツマーケティングの専門家です。以下の情報を分析して、詳細な記事構成を作成してください：

【基本情報】
- メインキーワード: {$mainKeyword}
- サブキーワード: {$subKeywords}
- 記事タイトル: {$articleTitle}
- テーマ: {$theme}
- ターゲットペルソナ: {$targetPersona}
- 検索意図: {$searchIntent}
- 目標文字数: {$targetWords}文字
- 目標セクション数: {$targetSections}個 （重要：必ずこの数に合わせてください）

【分析要求】
以下の項目を詳細に分析し、データに基づいて回答してください：

1. 文字数配分分析：
   - 目標文字数に対する最適な構成
   - 各セクションの推奨文字数
   - SEO効果を最大化する文字数戦略

2. 画像配置計画：
   - 推奨画像数と配置間隔
   - ユーザーエンゲージメントを向上させる画像戦略
   - 各セクションでの画像の必要性

3. 競合比較分析：
   - 同じキーワードでの競合サイトの平均文字数予測
   - 上位サイトの推定文字数
   - 競合に対する優位性戦略

4. 記事構成リスト：
   - {$targetWords}文字、{$targetSections}セクションに最適化された見出し構成
   - 各見出しの推奨文字数
   - 画像配置の必要性

以下のJSON形式で結果を返してください：
{
  \"targetWords\": {$targetWords},
  \"headings\": [
    {
      \"title\": \"見出し1\",
      \"wordCount\": 500,
      \"hasImage\": true
    }
  ],
  \"recommendedImages\": \"4-5\",
  \"imageInterval\": \"600-750\",
  \"competitorAvgWords\": 2800,
  \"topSiteWords\": 3200,
  \"advantage\": \"競合優位性の説明\"
}

重要：headings配列は必ず{$targetSections}個の要素を含むように作成してください。JSONのみを返し、他のテキストや説明は追加しないでください。";
	}

	/**
	 * Calculate dynamic max_tokens based on target word count
	 */
	private function calculateMaxTokens(int $targetWords, string $type = 'article'): int
	{
		switch ($type) {
			case 'article':
				// For article generation: ~1.3-1.5 tokens per word + buffer
				return min(max((int)($targetWords * 1.4 + 2000), 4000), 32000);
				
			case 'structure':
				// For brief/outline generation: fixed reasonable amount
				return (int)config('anthropic.max_tokens.structure', 4000);
				
			case 'analysis':
				// For analysis: moderate amount
				return 3000;
				
			default:
				return 8000;
		}
	}
	
	/**
	 * Generate AI outline using Claude API directly
	 */
	private function generateAIOutline(string $prompt, string $keyword, array $briefData, int $targetWords): array
	{
		try {
			// Calculate dynamic max_tokens for structure generation
			$maxTokens = $this->calculateMaxTokens($targetWords, 'structure');
			
			// Call Claude AI API directly
			$response = Anthropic::messages()->create([
				'model' => config('anthropic.model', 'claude-3-sonnet-20240229'),
				'max_tokens' => $maxTokens,
				'messages' => [
					[
						'role' => 'user',
						'content' => $prompt
					]
				],
				'temperature' => 0.5  // Reduced for more consistent output
			]);
			
			// Parse AI response
			$aiContent = $response->content[0]->text ?? '';
			$aiContent = preg_replace('/^```json\\s*/i', '', $aiContent);
			$aiContent = preg_replace('/\\s*```$/i', '', $aiContent);
			$aiContent = trim($aiContent);
			
			// Parse JSON response from AI
			$outlineData = json_decode($aiContent, true);
			
			if (!$outlineData || !isset($outlineData['headings'])) {
				Log::error('Failed to parse AI outline response', ['raw_content' => $aiContent]);
				throw new \Exception('Invalid AI response format');
			}
			
			// Ensure required fields are present with defaults if missing
			return [
				'targetWords'        => $outlineData['targetWords'] ?? $targetWords,
				'headings'           => $outlineData['headings'] ?? [],
				'recommendedImages'  => $outlineData['recommendedImages'] ?? '3-4',
				'imageInterval'      => $outlineData['imageInterval'] ?? '500-700',
				'competitorAvgWords' => $outlineData['competitorAvgWords'] ?? intval($targetWords * 0.8),
				'topSiteWords'       => $outlineData['topSiteWords'] ?? intval($targetWords * 1.2),
				'advantage'          => $outlineData['advantage'] ?? '詳細な分析による差別化',
			];
			
		} catch (\Exception $e) {
			Log::error('AI Outline Generation Failed', [
				'error' => $e->getMessage(),
				'keyword' => $keyword
			]);
			
			// Fallback to basic structure if AI fails
			return $this->getFallbackOutlineData($keyword, $briefData, $targetWords);
		}
	}
	
	/**
	 * Fallback outline data when AI fails
	 */
	private function getFallbackOutlineData(string $keyword, array $briefData, int $targetWords): array
	{
		$searchIntent = $briefData['searchIntent'] ?? 'Informational';
		
		// Basic fallback structure based on search intent
		$baseStructures = [
			'Informational' => [
				['template' => '{keyword}とは？基本概要', 'ratio' => 0.20, 'image' => true],
				['template' => '{keyword}のメリット・効果', 'ratio' => 0.25, 'image' => true],
				['template' => '{keyword}の具体的な方法', 'ratio' => 0.30, 'image' => true],
				['template' => '{keyword}の注意点', 'ratio' => 0.15, 'image' => false],
				['template' => 'まとめ', 'ratio' => 0.10, 'image' => false],
			],
			'Commercial'    => [
				['template' => '{keyword}の選び方', 'ratio' => 0.25, 'image' => true],
				['template' => 'おすすめ{keyword}比較', 'ratio' => 0.30, 'image' => true],
				['template' => '{keyword}の価格・料金', 'ratio' => 0.20, 'image' => false],
				['template' => '{keyword}導入方法', 'ratio' => 0.15, 'image' => true],
				['template' => 'まとめ', 'ratio' => 0.10, 'image' => false],
			],
		];
		
		$structure = $baseStructures[$searchIntent] ?? $baseStructures['Informational'];
		
		$headings = array_map(function ($item) use ($keyword, $targetWords) {
			return [
				'title'     => str_replace('{keyword}', $keyword, $item['template']),
				'wordCount' => intval($targetWords * $item['ratio']),
				'hasImage'  => $item['image'],
			];
		}, $structure);
		
		return [
			'targetWords'        => $targetWords,
			'headings'           => $headings,
			'recommendedImages'  => '3-4',
			'imageInterval'      => '500-700',
			'competitorAvgWords' => intval($targetWords * 0.8),
			'topSiteWords'       => intval($targetWords * 1.2),
			'advantage'          => '詳細な分析による差別化',
		];
	}
	
	/**
	 * Generate article content with streaming (API endpoint)
	 */
	public function generateArticle(Request $request)
	{
		$request->validate([
			'keyword'         => 'required|array',
			'keyword.id'      => 'required|integer',
			'keyword.keyword' => 'required|string',
			'briefData'       => 'required|array',
			'outlineData'     => 'required|array',
			'analysisData'    => 'required|array',
		]);
		
		return new \Symfony\Component\HttpFoundation\StreamedResponse(function () use ($request) {
			try {
				$keyword = $request->input('keyword.keyword');
				$briefData = $request->input('briefData');
				$outlineData = $request->input('outlineData');
				$analysisData = $request->input('analysisData');
				
				// Build AI prompt for article generation with analysis data
				$prompt = $this->buildArticlePrompt($keyword, $briefData, $outlineData, $analysisData);
				
				// Stream TipTap-formatted article content
				$this->streamArticleContent($prompt, $keyword, $briefData, $outlineData, $analysisData);
				
			} 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',
		]);
	}
	
	/**
	 * Build AI prompt for article generation
	 */
	private function buildArticlePrompt(string $keyword, array $briefData, array $outlineData, array $analysisData): string
	{
		$title = $briefData['articleTitle'];
		$mainKeyword = $briefData['mainKeyword'];
		$subKeywords = $briefData['subKeywords'];
		$theme = $briefData['theme'];
		$targetPersona = $briefData['targetPersona'];
		$searchIntent = $briefData['searchIntent'];
		$targetWords = $analysisData['targetWords'];
		$competitorAvgWords = $analysisData['competitorAvgWords'];
		$topSiteWords = $analysisData['topSiteWords'];
		$advantage = $analysisData['advantage'];
		$recommendedImages = $analysisData['recommendedImages'];
		$imageInterval = $analysisData['imageInterval'];

		// Get FAQs from briefData (Step 2)
		$faqs = $briefData['faqs'] ?? [];

		// Generate unique featured image ID using ImageIdGeneratorService
		$imageIdGenerator = app(ImageIdGeneratorService::class);
		$featuredImageId = $imageIdGenerator->generateFeaturedImageId($title, time());

		$headingsText = '';
		foreach ($outlineData['headings'] as $index => $heading) {
			$headingsText .= ($index + 1) . '. ' . $heading['title'] . ' (' . $heading['wordCount'] . '文字)' . "\n";
		}

		// Build FAQ text from existing FAQs
		$faqText = '';
		if (!empty($faqs)) {
			$faqText = "【記事に含めるQ&A（必ず使用）】\n";
			foreach ($faqs as $index => $faq) {
				$faqText .= "Q" . ($index + 1) . ": " . $faq['question'] . "\n";
				$faqText .= "A: " . $faq['answer'] . "\n\n";
			}
		}
		
		return "あなたはプロのSEOライター兼コンテンツマーケティング専門家です。以下の詳細な分析データを基に、完全なSEO最適化されたHTML記事を作成してください：

【記事基本情報】
- 記事タイトル: {$title}
- メインキーワード: {$mainKeyword}
- サブキーワード: {$subKeywords}
- テーマ: {$theme}
- ターゲットペルソナ: {$targetPersona}
- 検索意図: {$searchIntent}

【SEO戦略データ】
- 目標文字数: {$targetWords}文字（競合平均{$competitorAvgWords}文字を上回る）
- 上位サイト文字数: {$topSiteWords}文字
- 競合優位戦略: {$advantage}
- 画像戦略: {$recommendedImages}枚、{$imageInterval}文字間隔で配置

【必須記事構成（この順番で必ず作成）】
{$headingsText}

{$faqText}

【HTML構造要件】
1. **記事タイトル**: <h1>{$title}</h1> で開始
2. **導入文（リード文）**: 200-300文字でメインキーワードを含む魅力的な導入
3. **リード文の後に必須セクション**:
   - **この記事で分かること**: 箇条書き（<ul><li>）で5つのポイントを記載
   - **この記事を読んでほしい人**: 箇条書き（<ul><li>）で5つのターゲット読者を記載
4. **各セクション**: 上記構成リストに従って順番に作成
5. **見出し階層**: H1(タイトル) > H2(主要セクション) > H3(サブセクション) > H4(詳細)
6. **カエルDXコンサルタントからの見解**: 記事の中盤（2-3個目のH2セクション後）に配置
7. **Q&Aセクション（重要）**: まとめの前に必ず配置
   - **上記【記事に含めるQ&A】に記載されているすべてのQ&Aを必ず使用すること**
   - 各質問はH3タグ、回答は段落（<p>）タグで記述
   - 提供されているQ&Aをそのまま正確に記事に含めること
   - Q&Aが提供されていない場合のみ、記事内容に関連する3-5個の質問と回答を作成
8. **結論部分（まとめ）**: 最後にまとめとコールトゥアクション

【SEO最適化要件】
- **メインキーワード「{$mainKeyword}」の使用（必須）**:
  - 記事全体で2-3%密度を維持し、自然な配置を心がける
  - **必ず以下の箇所に含める**:
    * 導入文（最初の段落）
    * 各主要セクション（H2見出し）の本文中
    * 結論部分
  - メインキーワードを省略せず、必ず記事内で繰り返し使用すること
- **サブキーワード「{$subKeywords}」の使用（必須）**:
  - 関連セクションで自然に組み込む
  - メインキーワードとの組み合わせで使用することを推奨
- **キーワード「{$keyword}」を記事全体で必ず使用すること（重要）**
- タイトルタグ階層の正しい使用 (H1→H2→H3→H4)
- 各セクションでキーワードバリエーションを活用
- {$advantage}を反映した独自性のある内容
- 競合を上回る{$targetWords}文字の詳細な内容

【TipTap互換HTML形式】
- 純粋なHTMLタグのみ（Markdown禁止）
- 使用可能タグ: h1, h2, h3, h4, p, strong, em, ul, ol, li, blockquote, img, br
- **記事の最初（h1タイトルの直後）に必ず以下の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見出しの直後に配置すること**
  - H2とその直後の画像の間に他のコンテンツ（段落など）を入れないこと
  - 画像の後に段落やコンテンツを配置すること
- **各構成セクション（h2見出し）に1つの画像を配置**
- 「まとめ」「結論」「総括」「Q&A」セクションには画像を配置しない
- 各セクション内画像には以下の形式を使用：
  <img id=\"img-{8文字のランダム文字列}\" src=\"/storage/uploads/placeholder.png\" alt=\"{セクション内容の詳細な画像説明を英語で}\" style=\"width: 100%; height: auto; margin: 20px 0; border-radius: 8px; display: none;\" />
- **alt属性には画像の詳細な説明を英語で記述**（画像生成AIが理解できる具体的な内容）
- **alt属性は必ず英語で記述し、日本語や他の言語は使用しない**

【コンテンツ品質要件】
- {$targetPersona}に向けた専門的で実用的な内容
- {$searchIntent}のニーズに完全対応
- 実例、具体例、統計データを含む
- 読者の行動を促すコールトゥアクション
- **記事全体を通じて、必ずキーワード「{$keyword}」を適切に使用すること**
- **各セクションでキーワードまたはその関連語を自然に含めること**

【出力形式要件（重要）】
- **改行ルール（必須）**:
  - すべての段落は1～3行で改行すること
  - 長い段落は避け、読みやすさを最優先すること
  - 段落間は自然な流れを保ちつつ、適度な改行を入れること
- **段落形式（<p>タグ）を主とし、箇条書き（<ul><li>）は最小限に抑制**
  - 例外: 「この記事で分かること」「この記事を読んでほしい人」「Q&A」は箇条書き可
- 各セクションは詳細な段落で構成し、箇条書きは補助的にのみ使用
- 情報は文章形式で自然に説明し、リスト形式は避ける
- 読みやすい段落構造を重視し、一貫した文章スタイルを保持

**記事構成例：**
<h1>{$title}</h1>
<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>導入文の1～3行目...（必ずキーワード「{$keyword}」を含める）</p>

<!-- この記事で分かること -->
<h2>この記事で分かること</h2>
<ul>
<li>ポイント1</li>
<li>ポイント2</li>
<li>ポイント3</li>
<li>ポイント4</li>
<li>ポイント5</li>
</ul>

<!-- この記事を読んでほしい人 -->
<h2>この記事を読んでほしい人</h2>
<ul>
<li>対象者1</li>
<li>対象者2</li>
<li>対象者3</li>
<li>対象者4</li>
<li>対象者5</li>
</ul>

<!-- 通常のセクション -->
<h2>セクション1タイトル</h2>
<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;\" />
<p>セクション1の内容1～3行目...（キーワード「{$keyword}」を自然に使用）</p>
<p>セクション1の内容続き...</p>

<h2>セクション2タイトル</h2>
<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;\" />
<p>セクション2の内容1～3行目...（キーワード「{$keyword}」を自然に使用）</p>
<p>セクション2の内容続き...</p>

<!-- カエルDXコンサルタントの見解（記事中盤に1回） -->
<blockquote class=\"consultant-opinion\" style=\"border-left: 4px solid #4CAF50; padding-left: 16px; margin: 24px 0; background-color: #f0f9ff;\">
<h3>カエルDXコンサルタントからの見解</h3>
<p>専門家の視点からの具体的なアドバイスや見解...</p>
</blockquote>

<!-- 他のセクション続き... -->

<!-- Q&A（まとめの前に必須） - 上記で提供されたQ&Aを使用 -->
<h2>よくある質問（Q&A）</h2>
<!-- 【記事に含めるQ&A】に記載された各Q&Aをそのまま正確に使用 -->
<h3>Q1: [提供された質問1をそのまま使用]</h3>
<p>A: [提供された回答1をそのまま使用]</p>
<h3>Q2: [提供された質問2をそのまま使用]</h3>
<p>A: [提供された回答2をそのまま使用]</p>
<!-- 以降、提供されたすべてのQ&Aを順番に記載 -->

<!-- まとめ -->
<h2>まとめ</h2>
<p>まとめの内容...</p>

**重要な注意事項：**
1. **記事の必須構成順序**:
   - H1タイトル
   - Featured画像
   - リード文（導入文）
   - 「この記事で分かること」（H2 + 箇条書き5つ）
   - 「この記事を読んでほしい人」（H2 + 箇条書き5つ）
   - 通常のセクション（H2 + 画像 + 段落内容）
   - カエルDXコンサルタントからの見解（blockquote、記事中盤に1回）
   - 続きのセクション
   - よくある質問（Q&A）（H2 + H3質問 + 段落回答、3-5個）
   - まとめ（H2 + 段落内容）

2. **画像配置ルール**:
   - すべての画像は必ずH2見出しの直後に配置
   - H2と画像の間に段落を入れない
   - 「この記事で分かること」「この記事を読んでほしい人」「Q&A」「まとめ」には画像を配置しない

3. **改行ルール**:
   - すべての段落（<p>タグ）は1～3行で改行
   - 長い文章は複数の<p>タグに分割

4. **キーワード使用**:
   - 記事全体を通じて、キーワード「{$keyword}」を必ず複数回使用
   - 各主要セクション（H2）の本文に、キーワードまたはその関連語を少なくとも1回含める
   - 導入文と結論部分には必ずキーワードを含める
   - キーワードの使用は自然で読みやすい文脈で行う

5. **カエルDXコンサルタントの見解**:
   - 記事の中盤（2-3個目のH2セクションの後）に1回だけ配置
   - blockquoteタグを使用し、専門家の視点からの具体的なアドバイスを記載

6. **Q&Aの使用（最重要）**:
   - 上記【記事に含めるQ&A】に記載されているすべての質問と回答を、そのまま正確に使用すること
   - 質問や回答の内容を変更したり、省略したり、言い換えたりしないこと
   - 提供された順番のままQ&Aを記事に含めること
   - Q&Aが提供されていない場合のみ、新しいQ&Aを作成すること

以下の構成で{$targetWords}文字の完全な記事を作成してください：

<h1>{$title}</h1>から開始し、上記の必須構成順序に従って各セクションを作成してください。必ずすべての要素（「この記事で分かること」「この記事を読んでほしい人」「カエルDXコンサルタントからの見解」「Q&A（提供されたQ&Aを使用）」）を含めてください。";
	}
	
	/**
	 * Stream article content using Claude API directly
	 */
	private function streamArticleContent(string $prompt, string $keyword, array $briefData, array $outlineData, array $analysisData): void
	{
		try {
			// Get target words for dynamic token calculation
			$targetWords = $outlineData['targetWords'] ?? (int)($briefData['wordCount'] ?? 4500);
			$maxTokens = $this->calculateMaxTokens($targetWords, 'article');
			
			// Call Claude AI streaming API directly
			$stream = Anthropic::messages()->createStreamed([
				'model'       => config('anthropic.model', 'claude-3-sonnet-20240229'),
				'max_tokens'  => $maxTokens, // Use dynamic calculation
				'messages'    => [
					[
						'role'    => 'user',
						'content' => $prompt
					]
				],
				'temperature' => 0.3  // Reduced for more consistent output
			]);
			
			$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();
				}
			}
			
			// Log the full content for debugging
			Log::info('Article generation completed', [
				'keyword'        => $keyword,
				'content_length' => strlen($fullContent),
				'max_tokens'     => $maxTokens
			]);
			
			// Send completion signal
			echo "data: " . json_encode([
					'type' => 'done',
					'data' => ['message' => '記事生成が完了しました'],
				]) . "\n\n";
			
		} catch (\Exception $e) {
			Log::error('AI Article Generation Failed', [
				'error'   => $e->getMessage(),
				'keyword' => $keyword
			]);
			
			// Send error signal
			echo "data: " . json_encode([
					'type' => 'error',
					'data' => ['message' => 'AI記事生成に失敗しました: ' . $e->getMessage()],
				]) . "\n\n";
		}
	}
	
	/**
	 * Generate article sections based on outline
	 */
	private function generateArticleSections(string $keyword, array $briefData, array $headings): array
	{
		$sections = [];
		$mainKeyword = $briefData['mainKeyword'];
		$subKeywords = $briefData['subKeywords'];
		$title = $briefData['articleTitle'];
		$targetPersona = $briefData['targetPersona'];
		$searchIntent = $briefData['searchIntent'];
		$theme = $briefData['theme'];
		
		// Generate unique featured image ID using ImageIdGeneratorService
		$imageIdGenerator = app(ImageIdGeneratorService::class);
		$featuredImageId = $imageIdGenerator->generateFeaturedImageId($title, time());
		
		// Article Title (H1)
		$sections[] = "<h1>" . $title . "</h1>";
		
		// Featured Image (immediately after H1)
		$featuredAlt = "Main illustration representing " . $title;
		$sections[] = "<img id=\"{$featuredImageId}\" class=\"featured-img\" src=\"/storage/uploads/placeholder.png\" alt=\"{$featuredAlt}\" style=\"width: 100%; height: auto; margin: 20px 0; border-radius: 8px; display: none;\" />";
		
		// SEO-optimized introduction (200-300 words)
		$sections[] = "<p>" . $mainKeyword . "について" . $targetPersona . "の方が知っておくべき重要な情報を、専門的な視点から詳しく解説します。</p>";
		$sections[] = "<p>この記事では、" . $theme . "に関する最新の知見と実践的なノウハウをお伝えし、" . $searchIntent . "のニーズに完全対応します。" . $subKeywords . "の要素も含めて、包括的にご説明いたします。</p>";
		
		// Generate sections based on outline structure from Step 3
		foreach ($headings as $index => $heading) {
			$headingTitle = $heading['title'];
			$wordCount = $heading['wordCount'];
			$hasImage = $heading['hasImage'];
			
			// Main section heading (H2)
			$sections[] = "<h2>" . $headingTitle . "</h2>";
			
			// Generate content based on specified word count
			$paragraphCount = max(2, intval($wordCount / 120)); // More paragraphs for better readability
			
			for ($p = 0; $p < $paragraphCount; $p++) {
				$content = $this->generateSEOOptimizedContent($mainKeyword, $subKeywords, $headingTitle, $wordCount / $paragraphCount, $index, $p);
				$sections[] = "<p>" . $content . "</p>";
			}
			
			// Add structured content based on section type
			if (strpos($headingTitle, '方法') !== false || strpos($headingTitle, '手順') !== false || strpos($headingTitle, 'ステップ') !== false) {
				// How-to sections get ordered lists
				$sections[] = "<h3>実践的な" . str_replace(['方法', '手順'], '', $headingTitle) . "の手順</h3>";
				$sections[] = "<ol>";
				$sections[] = "<li><strong>準備段階：</strong>" . $mainKeyword . "に必要な基本的な準備を整えます</li>";
				$sections[] = "<li><strong>実装段階：</strong>具体的な手順に従って段階的に進めます</li>";
				$sections[] = "<li><strong>検証段階：</strong>結果を評価し、必要に応じて調整を行います</li>";
				$sections[] = "<li><strong>最適化段階：</strong>継続的な改善で効果を最大化します</li>";
				$sections[] = "</ol>";
			} elseif (strpos($headingTitle, 'メリット') !== false || strpos($headingTitle, '効果') !== false || strpos($headingTitle, '利点') !== false) {
				// Benefits sections get bullet lists
				$sections[] = "<h3>" . $mainKeyword . "の主な利点</h3>";
				$sections[] = "<ul>";
				$sections[] = "<li><strong>コスト効率：</strong>従来の方法と比較して大幅なコスト削減が実現</li>";
				$sections[] = "<li><strong>時間短縮：</strong>作業効率の向上により時間を有効活用</li>";
				$sections[] = "<li><strong>品質向上：</strong>より高い品質基準を満たす結果を達成</li>";
				$sections[] = "<li><strong>スケーラビリティ：</strong>規模の拡張に対応可能な柔軟性</li>";
				$sections[] = "</ul>";
			} elseif (strpos($headingTitle, '注意') !== false || strpos($headingTitle, 'ポイント') !== false || strpos($headingTitle, '重要') !== false) {
				// Important points get blockquotes
				$sections[] = "<blockquote><p><strong>重要：</strong>" . $mainKeyword . "を成功させるためには、" . $subKeywords . "の要素を適切に理解し、継続的な改善に取り組むことが不可欠です。</p></blockquote>";
			}
			
			// Add image if specified in outline
			if ($hasImage) {
				// Generate unique ID for content image (8 random characters)
				$uniqueId = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 8);
				$imageId = "img-{$uniqueId}";
				
				$imageAlt = "Professional illustration demonstrating " . strtolower(str_replace([$mainKeyword, 'の', 'について', 'とは'], '', $headingTitle)) . " concepts and implementation";
				$sections[] = "<img id=\"{$imageId}\" src=\"/storage/uploads/placeholder.png\" alt=\"{$imageAlt}\" style=\"width: 100%; height: auto; margin: 20px 0; border-radius: 8px; display: none;\" />";
			}
			
			// Add subsection for complex topics
			if ($wordCount > 400) {
				$subSectionTitle = $this->generateSubSectionTitle($headingTitle, $mainKeyword);
				$sections[] = "<h3>" . $subSectionTitle . "</h3>";
				$subContent = $this->generateSEOOptimizedContent($mainKeyword, $subKeywords, $subSectionTitle, 200, $index, $paragraphCount);
				$sections[] = "<p>" . $subContent . "</p>";
			}
		}
		
		// Conclusion section with CTA
		$sections[] = "<h2>まとめ：" . $mainKeyword . "で成功するために</h2>";
		$sections[] = "<p>以上、" . $mainKeyword . "について" . $targetPersona . "向けに詳しく解説しました。" . $searchIntent . "に応じた適切な戦略により、大きな成果を期待できます。</p>";
		$sections[] = "<p><strong>今すぐ行動：</strong>" . $theme . "の実装を検討されている方は、まず基本的な準備から始めて、段階的に進めることをお勧めします。" . $subKeywords . "の要素も忘れずに取り入れましょう。</p>";
		
		return $sections;
	}
	
	/**
	 * Generate TipTap compatible paragraph content
	 */
	private function generateTipTapParagraphContent(string $keyword, string $mainKeyword, string $subKeywords, string $heading, int $targetLength): string
	{
		$templates = [
			"{$mainKeyword}における{$heading}について、実際の事例を交えながら詳しく説明します。多くの企業が<strong>この手法</strong>を採用することで、大幅な効率向上を実現しています。具体的な手順としては、まず現状分析を行い、目標設定を明確にした上で、段階的に実装を進めることが重要です。{$subKeywords}の要素も考慮に入れることで、より効果的な結果が期待できます。",
			"この{$heading}の重要性は、近年ますます高まっています。{$mainKeyword}を効果的に活用するためには、<em>適切な戦略立案</em>と継続的な改善が欠かせません。成功事例を見ると、共通して見られるのは、チーム全体での理解共有と、データに基づいた意思決定プロセスの確立です。{$subKeywords}を意識したアプローチにより、さらなる向上が可能になります。",
			"{$heading}を実践する際の注意点として、初期段階での準備が<strong>非常に重要</strong>になります。{$mainKeyword}に関する基礎知識を身につけ、実際の導入前に十分な検討を行うことで、後々のトラブルを避けることができます。また、{$subKeywords}の観点からも定期的な評価と改善サイクルを設けることが、長期的な成功には不可欠です。"
		];
		
		$content = $templates[array_rand($templates)];
		
		// Adjust length to match target (maintain HTML tags)
		if (mb_strlen(strip_tags($content)) > $targetLength) {
			// Truncate while preserving HTML structure
			$plainText = strip_tags($content);
			$truncated = mb_substr($plainText, 0, $targetLength - 3) . '...';
			// Re-apply basic formatting
			$content = str_replace($plainText, $truncated, $content);
		} elseif (mb_strlen(strip_tags($content)) < $targetLength * 0.8) {
			$content .= "さらに詳しい内容については、<strong>専門的な観点</strong>から継続的な研究と実践が必要となります。";
		}
		
		return $content;
	}
	
	/**
	 * Generate SEO optimized content
	 */
	private function generateSEOOptimizedContent(string $mainKeyword, string $subKeywords, string $heading, int $targetLength, int $sectionIndex, int $paragraphIndex): string
	{
		$templates = [
			"{$mainKeyword}における{$heading}について、最新の研究と実践事例を基に詳しく解説します。多くの専門家が<strong>この手法</strong>を推奨する理由として、効果的な結果が得られることが挙げられます。{$subKeywords}の観点からも、戦略的なアプローチが重要となります。実際の導入事例では、従来の方法と比較して大幅な改善が報告されています。",
			"この{$heading}の重要性は、業界全体で<em>ますます高まっています</em>。{$mainKeyword}を効果的に活用するためには、適切な計画立案と段階的な実装が不可欠です。{$subKeywords}を考慮した戦略により、より効果的な成果を期待できます。成功事例の分析から、継続的な改善と最適化が成功の鍵であることが明らかになっています。",
			"{$heading}を実践する際の<strong>ベストプラクティス</strong>として、初期段階での十分な準備が挙げられます。{$mainKeyword}に関する基礎知識を身につけ、{$subKeywords}の要素を適切に組み込むことで、より良い結果を得ることができます。専門家の意見では、定期的な評価と改善サイクルの確立が、長期的な成功には不可欠とされています。"
		];
		
		$baseContent = $templates[$paragraphIndex % count($templates)];
		
		// Add section-specific content
		if ($sectionIndex === 0) {
			$baseContent = "はじめに、" . $baseContent;
		} elseif (strpos($heading, 'まとめ') !== false || strpos($heading, '結論') !== false) {
			$baseContent = "最後に、" . $baseContent;
		}
		
		// Adjust length to match target
		if (mb_strlen(strip_tags($baseContent)) > $targetLength) {
			$plainText = strip_tags($baseContent);
			$truncated = mb_substr($plainText, 0, $targetLength - 10) . 'ということです。';
			$baseContent = str_replace($plainText, $truncated, $baseContent);
		} elseif (mb_strlen(strip_tags($baseContent)) < $targetLength * 0.8) {
			$baseContent .= "さらに詳細な情報については、<strong>専門的な分析</strong>と継続的な研究が必要となります。";
		}
		
		return $baseContent;
	}
	
	/**
	 * Generate subsection title
	 */
	private function generateSubSectionTitle(string $mainTitle, string $mainKeyword): string
	{
		$subTitles = [
			$mainKeyword . 'の詳細解説',
			'具体的な' . str_replace(['について', 'とは', 'の'], '', $mainTitle) . '事例',
			'実践における' . $mainKeyword . 'のポイント',
			$mainTitle . 'の最新動向',
			'効果的な' . str_replace(['について', 'とは'], '', $mainTitle) . '手法'
		];
		
		return $subTitles[array_rand($subTitles)];
	}
	
	/**
	 * Check article quality (Step 5: チェック)
	 */
	public function checkArticle(Request $request)
	{
		try {
			$validated = $request->validate([
				'keyword'      => 'required|string',
				'title'        => 'required|string',
				'content'      => 'required|string',
				'brief_data'   => 'required|array',
				'outline_data' => 'required|array'
			]);
			
			$keyword = $validated['keyword'];
			$title = $validated['title'];
			$content = $validated['content'];
			$briefData = $validated['brief_data'];
			$outlineData = $validated['outline_data'];
			
			// First, get basic analysis
			$basicAnalysis = $this->analyzeArticleQuality($content, $keyword, $title, $briefData, $outlineData);
			
			// Then, get AI-powered deep analysis
			$aiAnalysis = $this->getAIArticleAnalysis($content, $keyword, $title, $basicAnalysis);
			
			// Merge both analyses
			$checkData = array_merge($basicAnalysis, $aiAnalysis);
			
			return response()->json([
				'success' => true,
				'data'    => $checkData,
				'message' => '記事チェックが完了しました'
			]);
			
		} catch (\Exception $e) {
			Log::error('Article check failed', [
				'error'   => $e->getMessage(),
				'keyword' => $request->input('keyword', 'N/A')
			]);
			
			// Return basic analysis if AI fails
			$basicAnalysis = $this->analyzeArticleQuality(
				$request->input('content', ''),
				$request->input('keyword', ''),
				$request->input('title', ''),
				$request->input('brief_data', []),
				$request->input('outline_data', [])
			);
			
			return response()->json([
				'success' => true,
				'data'    => $basicAnalysis,
				'message' => '基本チェックが完了しました（AI分析は利用できません）'
			]);
		}
	}
	
	/**
	 * Analyze article quality and generate metrics
	 */
	private function analyzeArticleQuality(string $content, string $keyword, string $title, array $briefData, array $outlineData): array
	{
		// Remove HTML tags for text analysis
		$plainContent = strip_tags($content);
		$contentLength = mb_strlen($plainContent);
		
		// SEO Analysis
		$keywordCount = substr_count(strtolower($plainContent), strtolower($keyword));
		$keywordDensity = $contentLength > 0 ? round((float)($keywordCount / mb_strlen($plainContent)) * 100, 1, PHP_ROUND_HALF_UP) : 0.0;
		
		// Title optimization (check if keyword is in title)
		$titleOptimization = strpos(strtolower($title), strtolower($keyword)) !== false ? 85 : 65;
		
		// Meta description analysis (based on content summary)
		$metaDescription = 92; // Default good score
		
		// Readability Analysis
		$sentences = preg_split('/[。！？]/', $plainContent, -1, PREG_SPLIT_NO_EMPTY);
		$sentenceCount = count($sentences);
		$avgSentenceLength = $sentenceCount > 0 ? intval($contentLength / $sentenceCount) : 0;
		
		// Count paragraphs (based on HTML structure)
		$paragraphCount = substr_count($content, '<p>') + substr_count($content, '<h');
		
		// Calculate readability score (based on sentence length and structure)
		$readabilityScore = $this->calculateReadabilityScore($avgSentenceLength, $paragraphCount, $contentLength);
		
		// Language composition analysis
		$languageStats = $this->analyzeLanguageComposition($plainContent);
		
		// Overall scores calculation
		$seoScore = intval(($keywordDensity * 30 + $titleOptimization * 0.4 + $metaDescription * 0.3));
		$seoScore = max(75, min(95, $seoScore)); // Ensure reasonable range
		
		$qualityScore = intval(($readabilityScore * 0.6 + $paragraphCount * 2 + 70));
		$qualityScore = max(80, min(92, $qualityScore));
		
		$originalityScore = rand(95, 99); // Simulate high originality
		
		$overallGrade = $this->calculateOverallGrade($seoScore, $qualityScore, $originalityScore);
		
		return [
			// SEO metrics
			'keywordDensity'    => $keywordDensity,
			'titleOptimization' => $titleOptimization,
			'metaDescription'   => $metaDescription,
			
			// Readability metrics
			'readabilityScore'  => $readabilityScore,
			'avgSentenceLength' => $avgSentenceLength,
			'paragraphCount'    => $paragraphCount,
			
			// Language composition
			'kanjiRate'         => $languageStats['kanjiRate'],
			'hiraganaRate'      => $languageStats['hiraganaRate'],
			'katakanaRate'      => $languageStats['katakanaRate'],
			
			// Originality score
			'originalityScore'  => $originalityScore,
			
			// Overall scores
			'seoScore'          => $seoScore,
			'qualityScore'      => $qualityScore,
			'overallGrade'      => $overallGrade
		];
	}
	
	/**
	 * Calculate readability score
	 */
	private function calculateReadabilityScore(int $avgSentenceLength, int $paragraphCount, int $contentLength): int
	{
		// Base score
		$score = 85;
		
		// Adjust based on sentence length (optimal: 30-50 characters)
		if ($avgSentenceLength < 20) {
			$score -= 10; // Too short
		} elseif ($avgSentenceLength > 60) {
			$score -= 15; // Too long
		}
		
		// Adjust based on paragraph structure
		if ($paragraphCount < 3) {
			$score -= 5; // Too few paragraphs
		} elseif ($paragraphCount > 15) {
			$score -= 3; // Too many paragraphs
		}
		
		// Content length bonus
		if ($contentLength > 2000) {
			$score += 5;
		}
		
		return max(70, min(95, $score));
	}
	
	/**
	 * Analyze language composition (Kanji, Hiragana, Katakana)
	 */
	private function analyzeLanguageComposition(string $text): array
	{
		$totalChars = mb_strlen($text);
		if ($totalChars === 0) {
			return ['kanjiRate' => 0, 'hiraganaRate' => 0, 'katakanaRate' => 0];
		}
		
		$kanjiCount = 0;
		$hiraganaCount = 0;
		$katakanaCount = 0;
		
		for ($i = 0; $i < $totalChars; $i++) {
			$char = mb_substr($text, $i, 1);
			
			// Check character type
			if (preg_match('/[\x{4E00}-\x{9FAF}]/u', $char)) {
				$kanjiCount++; // Kanji
			} elseif (preg_match('/[\x{3040}-\x{309F}]/u', $char)) {
				$hiraganaCount++; // Hiragana
			} elseif (preg_match('/[\x{30A0}-\x{30FF}]/u', $char)) {
				$katakanaCount++; // Katakana
			}
		}
		
		return [
			'kanjiRate'    => intval(($kanjiCount / $totalChars) * 100),
			'hiraganaRate' => intval(($hiraganaCount / $totalChars) * 100),
			'katakanaRate' => intval(($katakanaCount / $totalChars) * 100)
		];
	}
	
	/**
	 * Get AI-powered article analysis
	 */
	private function getAIArticleAnalysis(string $content, string $keyword, string $title, array $basicAnalysis): array
	{
		try {
			// Build prompt for AI analysis
			$prompt = $this->buildArticleAnalysisPrompt($content, $keyword, $title, $basicAnalysis);
			
			// Call Claude AI for deep analysis
			$response = Anthropic::messages()->create([
				'model'       => config('anthropic.model', 'claude-3-sonnet-20240229'),
				'max_tokens'  => (int)config('anthropic.max_tokens.structure', 2000),
				'messages'    => [
					[
						'role'    => 'user',
						'content' => $prompt
					]
				],
				'temperature' => 0.5
			]);
			
			// Parse AI response
			$aiContent = $response->content[0]->text ?? '';
			$aiContent = preg_replace('/^```json\s*/i', '', $aiContent);
			$aiContent = preg_replace('/\s*```$/i', '', $aiContent);
			$aiContent = trim($aiContent);
			
			// Parse JSON response from AI
			$aiAnalysis = json_decode($aiContent, true);
			
			if (!$aiAnalysis) {
				Log::error('Failed to parse AI analysis', ['raw_content' => $aiContent]);
				return [];
			}
			
			return $aiAnalysis;
			
		} catch (\Exception $e) {
			Log::error('AI Article Analysis Failed', [
				'error'   => $e->getMessage(),
				'keyword' => $keyword
			]);
			
			// Return empty array if AI analysis fails
			return [];
		}
	}
	
	/**
	 * Build prompt for AI article analysis
	 */
	private function buildArticleAnalysisPrompt(string $content, string $keyword, string $title, array $basicAnalysis): string
	{
		$plainContent = strip_tags($content);
		$contentLength = mb_strlen($plainContent);
		$keywordDensity = $basicAnalysis['keywordDensity'] ?? 0;
		
		return "あなたはSEOとコンテンツ品質の専門家です。以下の記事を詳細に分析し、品質評価を行ってください。

【記事情報】
- タイトル: {$title}
- メインキーワード: {$keyword}
- 文字数: {$contentLength}文字
- キーワード密度: {$keywordDensity}%

【記事本文（冒頭500文字）】
" . mb_substr($plainContent, 0, 500) . "...

【分析要求】
以下の観点から記事を詳細に評価してください：

1. SEO評価
   - キーワードの自然な使用
   - タイトルの最適化
   - 見出し構造の適切性
   - 内部リンクの可能性

2. コンテンツ品質
   - 情報の正確性と網羅性
   - 読みやすさと構成
   - ユーザーへの価値提供
   - 独自性と専門性

3. 技術的評価
   - 文章の論理性
   - 専門用語の適切な使用
   - 実例やデータの活用

4. 改善提案
   - 具体的な改善ポイント
   - 追加すべき情報
   - SEO改善の余地

以下のJSON形式で結果を返してください：
{
  \"seoScore\": 85,
  \"qualityScore\": 88,
  \"originalityScore\": 92,
  \"overallGrade\": \"A\",
  \"strengths\": [
    \"キーワードが自然に配置されている\",
    \"見出し構造が論理的\",
    \"実例が豊富で具体的\"
  ],
  \"weaknesses\": [
    \"内部リンクの機会を活用できていない\",
    \"一部の専門用語に説明が不足\"
  ],
  \"improvements\": [
    \"関連記事への内部リンクを追加\",
    \"図表やインフォグラフィックの追加を検討\",
    \"FAQ セクションの充実\"
  ],
  \"competitiveAnalysis\": \"競合記事と比較して情報量と専門性で優位。ただし、ビジュアル要素で改善の余地あり\",
  \"userEngagement\": \"高い滞在時間が期待できる構成。段階的な説明で初心者にも配慮\",
  \"searchRanking\": \"上位3位以内のランキングが期待できる品質。長期的な順位維持も可能\"
}

注意：JSONのみを返し、他のテキストや説明は追加しないでください。";
	}
	
	/**
	 * Calculate overall grade
	 */
	private function calculateOverallGrade(int $seoScore, int $qualityScore, int $originalityScore): string
	{
		$averageScore = ($seoScore + $qualityScore + $originalityScore) / 3;
		
		if ($averageScore >= 95) return 'A+';
		if ($averageScore >= 90) return 'A';
		if ($averageScore >= 85) return 'B+';
		if ($averageScore >= 80) return 'B';
		if ($averageScore >= 75) return 'C+';
		if ($averageScore >= 70) return 'C';
		
		return 'D';
	}
	
	/**
	 * Check for duplicate articles
	 */
	public function checkDuplicateArticles(Request $request)
	{
		$validator = Validator::make($request->all(), [
			'articles'         => 'required|array',
			'articles.*.title' => 'required|string',
			'articles.*.type'  => 'required|integer|in:0,1'
		]);
		
		if ($validator->fails()) {
			return response()->json([
				'success' => false,
				'message' => 'Validation failed',
				'errors'  => $validator->errors()
			], 422);
		}
		
		$articles = $request->input('articles');
		
		foreach ($articles as $article) {
			$exists = \App\Models\Article::where('title', $article['title'])
				->where('type', $article['type'])
				->exists();
			
			if ($exists) {
				return response()->json([
					'success'        => true,
					'hasDuplicates'  => true,
					'duplicateTitle' => $article['title']
				]);
			}
		}
		
		return response()->json([
			'success'       => true,
			'hasDuplicates' => false
		]);
	}
	
	/**
	 * Bulk save articles
	 */
	public function bulkSaveArticles(Request $request)
	{
		$validator = Validator::make($request->all(), [
			'articles'                       => 'required|array',
			'articles.*.title'               => 'required|string|max:255',
			'articles.*.slug'                => 'required|string|max:255',
			'articles.*.content'             => 'required|string',
			'articles.*.type'                => 'required|integer|in:0,1',
			'articles.*.categories'          => 'required|array|min:1',
			'articles.*.primary_category_id' => 'required',
			'articles.*.status'              => 'required|string|in:draft,publish',
			'articles.*.tags'                => 'nullable|array',
			'articles.*.description'         => 'nullable|string',
			'articles.*.excerpt'             => 'nullable|string',
			'articles.*.focus_keyword'       => 'nullable|string',
			'articles.*.image'               => 'nullable|string',
			'articles.*.og_image'            => 'nullable|string'
		]);
		
		if ($validator->fails()) {
			return response()->json([
				'success' => false,
				'message' => 'Validation failed',
				'errors'  => $validator->errors()
			], 422);
		}
		
		$articles = $request->input('articles');
		$savedCount = 0;
		$savedArticles = [];
		
		DB::beginTransaction();
		
		try {
			foreach ($articles as $articleData) {
				$primaryCategoryId = $articleData['primary_category_id'];
				if (is_array($primaryCategoryId)) {
					$primaryCategoryId = $primaryCategoryId[0];
				}
				
				// Create article
				$article = new \App\Models\Article();
				$article->type = $articleData['type'];
				$article->title = $articleData['title'];
				$article->slug = $articleData['slug'];
				$article->description = $articleData['description'] ?? null;
				$article->content = $articleData['content'];
				$article->excerpt = $articleData['excerpt'] ?? null;
				$article->primary_category_id = (int)$primaryCategoryId;
				$article->focus_keyword = $articleData['focus_keyword'] ?? null;
				$article->image = $articleData['image'] ?? null;
				$article->og_image = $articleData['og_image'] ?? null;
				$article->status = $articleData['status'];
				$article->user_id = auth()->id();
				$article->save();
				
				// Attach categories
				if (!empty($articleData['categories'])) {
					$article->categories()->attach($articleData['categories']);
				}
				
				// Handle tags
				if (!empty($articleData['tags'])) {
					$tagIds = [];
					$processedTags = [];
					
					foreach ($articleData['tags'] as $tagName) {
						$slug = Str::slug($tagName);
						$tagKey = $articleData['type'] . '-' . $slug;
						if (isset($processedTags[$tagKey])) {
							$tagIds[] = $processedTags[$tagKey];
							continue;
						}
						
						$tag = Tag::firstOrCreate(
							['type' => $articleData['type'], 'slug' => $slug],
							['name' => $tagName]
						);
						
						$tagIds[] = $tag->id;
						$processedTags[$tagKey] = $tag->id;
					}
					
					// Remove duplicates from tagIds first
					$tagIds = array_unique($tagIds);
					
					// Only attach tags that aren't already attached
					$existingTagIds = $article->tags()->pluck('tags.id')->toArray();
					$newTagIds = array_diff($tagIds, $existingTagIds);
					
					if (!empty($newTagIds)) {
						$article->tags()->attach($newTagIds);
					}
				}
				
				$savedCount++;
				$savedArticles[] = $article->id;
			}
			
			DB::commit();
			
			return response()->json([
				'success'    => true,
				'savedCount' => $savedCount,
				'articleIds' => $savedArticles,
				'message'    => "{$savedCount}件の記事を保存しました"
			]);
			
		} catch (\Exception $e) {
			DB::rollback();
			Log::error('Failed to save articles', [
				'error' => $e->getMessage(),
				'trace' => $e->getTraceAsString()
			]);
			
			return response()->json([
				'success' => false,
				'message' => '記事の保存に失敗しました: ' . $e->getMessage()
			], 500);
		}
	}
	
	/**
	 * Get categories for AI Writer
	 */
	public function getCategoriesForType($type)
	{
		if (!in_array($type, ['mattock', 'pipopa'])) {
			return response()->json([
				'success' => false,
				'message' => 'Invalid type'
			], 400);
		}
		
		$typeValue = $type === 'mattock' ? 0 : 1;
		
		$categories = \App\Models\ArticleCategory::where('type', $typeValue)
			->where('status', 1)
			->select('id', 'name')
			->orderBy('name')
			->get();
		
		return response()->json([
			'success'    => true,
			'categories' => $categories
		]);
	}
	
	/**
	 * Get all tags
	 */
	public function getAllTags()
	{
		$tags = \App\Models\ArticleTag::select('name')
			->distinct()
			->orderBy('name')
			->get();
		
		return response()->json([
			'success' => true,
			'tags'    => $tags
		]);
	}
	
	/**
	 * Fetch all categories for AI Writer
	 */
	public function fetchCategories()
	{
		try {
			$categories = Category::select('id', 'name', 'type', 'parent_id')
				->orderBy('type')
				->orderBy('name')
				->get();
			
			return response()->json($categories);
		} catch (\Exception $e) {
			Log::error('Failed to fetch categories: ' . $e->getMessage());
			return response()->json([]);
		}
	}
	
	/**
	 * Generate completion using Claude AI
	 */
	public function generateCompletion(Request $request)
	{
		$validator = Validator::make($request->all(), [
			'systemPrompt' => 'required|string',
			'userPrompt'   => 'required|string',
			'maxLength'    => 'nullable|integer|min:100|max:5000'
		]);
		
		if ($validator->fails()) {
			return response()->json([
				'success' => false,
				'message' => 'Validation failed',
				'errors'  => $validator->errors()
			], 422);
		}
		
		try {
			$maxTokens = $request->input('maxLength', 1000);
			
			$result = Anthropic::messages()->create([
				'model'      => 'claude-3-5-sonnet-20241022',
				'max_tokens' => $maxTokens,
				'system'     => $request->input('systemPrompt'),
				'messages'   => [
					['role' => 'user', 'content' => $request->input('userPrompt')]
				],
			]);
			
			return response()->json([
				'success' => true,
				'content' => $result->content[0]->text ?? ''
			]);
			
		} catch (\Exception $e) {
			Log::error('Claude API Error: ' . $e->getMessage());
			return response()->json([
				'success' => false,
				'message' => 'Failed to generate content',
				'error'   => $e->getMessage()
			], 500);
		}
	}
}
