import {
    Body,
    Controller,
    Delete,
    Get,
    Param,
    Post,
    Request,
    Res,
    UploadedFile,
    UseGuards,
    UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { Response } from 'express';
import { ApiKeyGuard } from '../auth/api-key.guard';
import { RagService } from '../rag/rag.service';
import { ChatService } from '../chat/chat.service';
import { KnowledgeBaseService } from '../knowledge-base/knowledge-base.service';
import { ModelConfigService } from '../model-config/model-config.service';
import { TenantService } from '../tenant/tenant.service';
import { UserSettingService } from '../user/user-setting.service';
import { I18nService } from '../i18n/i18n.service';
import { ApiTags, ApiOperation, ApiResponse, ApiSecurity, ApiConsumes, ApiBody, ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';

/** V1 API 聊天请求DTO */
class V1ChatDto {
  @ApiProperty({ description: '用户消息', example: '你好，请介绍一下你们的产品' })
  message: string;

  @ApiPropertyOptional({ description: '是否使用流式响应', example: false, default: false })
  stream?: boolean;

  @ApiPropertyOptional({ description: '选中的知识分组ID列表', example: [] })
  selectedGroups?: string[];

  @ApiPropertyOptional({ description: '选中的文件ID列表', example: [] })
  selectedFiles?: string[];
}

/** V1 API 搜索请求DTO */
class V1SearchDto {
  @ApiProperty({ description: '搜索关键词', example: '产品介绍' })
  query: string;

  @ApiPropertyOptional({ description: '返回结果数量', example: 5, default: 5 })
  topK?: number;

  @ApiPropertyOptional({ description: '相似度阈值', example: 0.3, default: 0.3 })
  threshold?: number;

  @ApiPropertyOptional({ description: '选中的知识分组ID列表' })
  selectedGroups?: string[];

  @ApiPropertyOptional({ description: '选中的文件ID列表' })
  selectedFiles?: string[];
}

/** V1 API 文件上传DTO */
class V1UploadDto {
  @ApiProperty({ description: '处理模式', enum: ['fast', 'precise'], example: 'fast' })
  mode?: 'fast' | 'precise';

  @ApiPropertyOptional({ description: '分块大小', example: 1000 })
  chunkSize?: number;

  @ApiPropertyOptional({ description: '分块重叠大小', example: 200 })
  chunkOverlap?: number;
}

@ApiTags('API v1')
@ApiSecurity('x-api-key')
@Controller('v1')
@UseGuards(ApiKeyGuard)
export class ApiV1Controller {
    constructor(
        private readonly ragService: RagService,
        private readonly chatService: ChatService,
        private readonly knowledgeBaseService: KnowledgeBaseService,
        private readonly modelConfigService: ModelConfigService,
        private readonly tenantService: TenantService,
        private readonly userSettingService: UserSettingService,
        private readonly i18nService: I18nService,
    ) { }

    // ========== Chat / RAG ==========
    /**
     * POST /api/v1/chat
     * Tenant-scoped RAG chat. Supports both streaming (SSE) and standard JSON responses.
     * Body: { message, stream?, selectedGroups?, selectedFiles? }
     */
    @Post('chat')
    @ApiOperation({ summary: 'RAG 聊天', description: '与知识库进行对话，支持流式(SSE)和非流式响应。使用API Key认证。' })
    @ApiBody({ type: V1ChatDto })
    @ApiResponse({ status: 200, description: '成功 - 流式返回或JSON响应' })
    @ApiResponse({ status: 400, description: '请求参数错误' })
    @ApiResponse({ status: 401, description: '认证失败' })
    async chat(
        @Request() req,
        @Body() body: V1ChatDto,
        @Res() res: Response,
    ) {
        const { message, stream = false, selectedGroups, selectedFiles } = body;
        const user = req.user;

        if (!message) {
            return res.status(400).json({ error: 'message is required' });
        }

        // Get organization settings and model configuration
        const tenantSettings = await this.tenantService.getSettings(user.tenantId);
        const userSetting = await this.userSettingService.getByUser(user.id);
        
        // Use the same logic as chat.controller.ts
        let llmModel;
        if (tenantSettings?.selectedLLMId) {
            // Find specifically selected model from tenant settings
            llmModel = await this.modelConfigService.findOne(tenantSettings.selectedLLMId);
        } else {
            // Use organization's default LLM from Index Chat Config
            llmModel = await this.modelConfigService.findDefaultByType(user.tenantId, 'llm' as any);
        }

        if (!llmModel) {
            return res.status(400).json({ error: 'No LLM model configured for this organization. Please set the default model in Admin Settings.' });
        }

        const modelConfig = llmModel as any;

        if (stream) {
            res.setHeader('Content-Type', 'text/event-stream');
            res.setHeader('Cache-Control', 'no-cache');
            res.setHeader('Connection', 'keep-alive');

            try {
                const stream = this.chatService.streamChat(
                    message,
                    [],                                       // history
                    user.id,
                    modelConfig,
                    userSetting?.language ?? 'zh',            // userLanguage
                    tenantSettings?.selectedEmbeddingId,      // selectedEmbeddingId
                    selectedGroups,                           // selectedGroups
                    selectedFiles,                            // selectedFiles
                    undefined,                                // historyId
                    tenantSettings?.enableRerank ?? false,    // enableRerank
                    tenantSettings?.selectedRerankId,         // selectedRerankId
                    tenantSettings?.temperature,              // temperature
                    tenantSettings?.maxTokens,                // maxTokens
                    tenantSettings?.topK ?? 5,                // topK
                    tenantSettings?.similarityThreshold ?? 0.3,   // similarityThreshold
                    tenantSettings?.rerankSimilarityThreshold ?? 0.5, // rerankSimilarityThreshold
                    tenantSettings?.enableQueryExpansion ?? false, // enableQueryExpansion
                    tenantSettings?.enableHyDE ?? false,           // enableHyDE
                    user.tenantId,                             // Passing tenantId correctly
                );

                for await (const chunk of stream) {
                    res.write(`data: ${JSON.stringify(chunk)}\n\n`);
                }
                res.write('data: [DONE]\n\n');
                res.end();
            } catch (error) {
                res.write(`data: ${JSON.stringify({ type: 'error', data: error.message })}\n\n`);
                res.end();
            }
        } else {
            // Non-streaming: collect all chunks and return as JSON
            try {
                let fullContent = '';
                const sources: any[] = [];
                let historyId: string | undefined;

                const chatStream = this.chatService.streamChat(
                    message,
                    [],
                    user.id,
                    modelConfig,
                    userSetting?.language ?? 'zh',
                    tenantSettings?.selectedEmbeddingId,
                    selectedGroups,
                    selectedFiles,
                    undefined,                                // historyId
                    tenantSettings?.enableRerank ?? false,
                    tenantSettings?.selectedRerankId,
                    tenantSettings?.temperature,
                    tenantSettings?.maxTokens,
                    tenantSettings?.topK ?? 5,
                    tenantSettings?.similarityThreshold ?? 0.3,
                    tenantSettings?.rerankSimilarityThreshold ?? 0.5,
                    tenantSettings?.enableQueryExpansion ?? false,
                    tenantSettings?.enableHyDE ?? false,
                    user.tenantId,                            // Passing tenantId correctly
                );

                for await (const chunk of chatStream) {
                    if (chunk.type === 'content') fullContent += chunk.data;
                    else if (chunk.type === 'sources') sources.push(...chunk.data);
                    else if (chunk.type === 'historyId') historyId = chunk.data;
                }

                return res.json({ content: fullContent, sources, historyId });
            } catch (error) {
                return res.status(500).json({ error: error.message });
            }
        }
    }

    // ========== Search ==========
    /**
     * POST /api/v1/search
     * Tenant-scoped hybrid search across knowledge base.
     * Body: { query, topK?, threshold?, selectedGroups?, selectedFiles? }
     */
    @Post('search')
    @ApiOperation({ summary: '知识库搜索', description: '在知识库中搜索相关内容，返回相似文档片段。使用API Key认证。' })
    @ApiBody({ type: V1SearchDto })
    @ApiResponse({ status: 200, description: '成功 - 返回搜索结果' })
    @ApiResponse({ status: 400, description: '请求参数错误' })
    @ApiResponse({ status: 401, description: '认证失败' })
    async search(
        @Request() req,
        @Body() body: V1SearchDto,
    ) {
        const { query, topK = 5, threshold = 0.3, selectedGroups, selectedFiles } = body;
        const user = req.user;

        if (!query) return { error: 'query is required' };

        const userSetting = await this.tenantService.getSettings(user.tenantId);

        const results = await this.ragService.searchKnowledge(
            query,
            user.id,
            topK,
            threshold,
            userSetting?.selectedEmbeddingId,
            userSetting?.enableFullTextSearch ?? false,
            userSetting?.enableRerank ?? false,
            userSetting?.selectedRerankId,
            selectedGroups,
            selectedFiles,
            userSetting?.rerankSimilarityThreshold ?? 0.5,
            user.tenantId,
            userSetting?.enableQueryExpansion ?? false,
            userSetting?.enableHyDE ?? false,
        );

        return { results, total: results.length };
    }

    // ========== Knowledge Base ==========
    /**
     * GET /api/v1/knowledge-bases
     * List all files belonging to the caller's tenant.
     */
    @Get('knowledge-bases')
    @ApiOperation({ summary: '列出知识库文件', description: '获取当前租户的所有知识库文件列表。使用API Key认证。' })
    @ApiResponse({ status: 200, description: '成功 - 返回文件列表' })
    @ApiResponse({ status: 401, description: '认证失败' })
    async listFiles(@Request() req) {
        const user = req.user;
        const files = await this.knowledgeBaseService.findAll(user.id, user.tenantId);
        return {
            files: files.map((f) => ({
                id: f.id,
                name: f.originalName,
                title: f.title,
                status: f.status,
                size: f.size,
                mimetype: f.mimetype,
                createdAt: f.createdAt,
            })),
            total: files.length,
        };
    }

    /**
     * POST /api/v1/knowledge-bases/upload
     * Upload and index a file into the caller's tenant knowledge base.
     */
    @Post('knowledge-bases/upload')
    @UseInterceptors(FileInterceptor('file'))
    @ApiOperation({ summary: '上传文件', description: '上传文件到知识库进行索引处理。使用API Key认证。' })
    @ApiConsumes('multipart/form-data')
    @ApiBody({
        schema: {
            type: 'object',
            properties: {
                file: { type: 'string', format: 'binary', description: '要上传的文件' },
                mode: { type: 'string', enum: ['fast', 'precise'], description: '处理模式：fast(快速)或precise(高精度)', example: 'fast' },
                chunkSize: { type: 'number', description: '分块大小', example: 1000 },
                chunkOverlap: { type: 'number', description: '分块重叠大小', example: 200 },
            },
        },
    })
    @ApiResponse({ status: 200, description: '成功 - 返回文件信息' })
    @ApiResponse({ status: 400, description: '请求参数错误' })
    @ApiResponse({ status: 401, description: '认证失败' })
    async uploadFile(
        @Request() req,
        @UploadedFile() file: Express.Multer.File,
        @Body() body: { mode?: 'fast' | 'precise'; chunkSize?: number; chunkOverlap?: number },
    ) {
        if (!file) return { error: 'file is required' };
        const user = req.user;

        const kb = await this.knowledgeBaseService.createAndIndex(
            file,
            user.id,
            user.tenantId,
            {
                mode: body.mode ?? 'fast',
                chunkSize: body.chunkSize ? Number(body.chunkSize) : 1000,
                chunkOverlap: body.chunkOverlap ? Number(body.chunkOverlap) : 200,
            },
        );

        return {
            id: kb.id,
            name: kb.originalName,
            status: kb.status,
            message: 'File uploaded and indexing started',
        };
    }

    /**
     * DELETE /api/v1/knowledge-bases/:id
     * Delete a specific file from the knowledge base.
     */
    @Delete('knowledge-bases/:id')
    @ApiOperation({ summary: '删除文件', description: '从知识库删除指定文件。使用API Key认证。' })
    @ApiResponse({ status: 200, description: '成功 - 文件已删除' })
    @ApiResponse({ status: 401, description: '认证失败' })
    @ApiResponse({ status: 404, description: '文件不存在' })
    async deleteFile(@Request() req, @Param('id') id: string) {
        const user = req.user;
        await this.knowledgeBaseService.deleteFile(id, user.id, user.tenantId);
        return { message: this.i18nService.getMessage('fileDeleted') };
    }

    @Get('knowledge-bases/:id')
    @ApiOperation({ summary: '获取文件详情', description: '获取指定文件的详细信息。使用API Key认证。' })
    @ApiResponse({ status: 200, description: '成功 - 返回文件详情' })
    @ApiResponse({ status: 404, description: '文件不存在' })
    async getFile(@Request() req, @Param('id') id: string) {
        const user = req.user;
        const files = await this.knowledgeBaseService.findAll(user.id, user.tenantId);
        const file = files.find((f) => f.id === id);
        if (!file) return { error: 'File not found' };
        return file;
    }
}
