import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Note } from './note.entity';
import { KnowledgeGroup } from '../knowledge-group/knowledge-group.entity';
import { OcrService } from '../ocr/ocr.service';
import * as fs from 'fs/promises';
import * as path from 'path';
import { v4 as uuidv4 } from 'uuid';
import { I18nService } from '../i18n/i18n.service';

@Injectable()
export class NoteService {
    // Directory will be created dynamically per tenant
    private getScreenshotsDir(tenantId: string) {
        return path.join(process.cwd(), 'uploads', tenantId, 'notes-screenshots');
    }

    constructor(
        @InjectRepository(Note)
        private readonly noteRepository: Repository<Note>,
        private readonly ocrService: OcrService,
        private readonly i18nService: I18nService,
    ) { }

    private async ensureScreenshotsDir(tenantId: string) {
        const dir = this.getScreenshotsDir(tenantId);
        try {
            await fs.access(dir);
        } catch {
            await fs.mkdir(dir, { recursive: true });
        }
    }

    async create(userId: string, tenantId: string, data: Partial<Note>): Promise<Note> {
        // Handle empty strings for foreign keys
        if (data.groupId === '') {
            data.groupId = null as any;
        }
        if (data.categoryId === '') {
            data.categoryId = null as any;
        }

        const note = this.noteRepository.create({
            ...data,
            userId,
            tenantId,
        });
        return this.noteRepository.save(note);
    }

    async findAll(userId: string, tenantId: string, isAdmin: boolean, groupId?: string, categoryId?: string): Promise<Note[]> {
        const query = this.noteRepository.createQueryBuilder('note')
            .leftJoinAndSelect('note.user', 'user')
            .where('note.tenantId = :tenantId', { tenantId })
            .select(['note', 'user.id', 'user.username'])
            .orderBy('note.updatedAt', 'DESC');

        if (!isAdmin) {
            query.andWhere('note.userId = :userId', { userId });
        }

        if (groupId) {
            query.andWhere('note.groupId = :groupId', { groupId });
        }

        if (categoryId) {
            query.andWhere('note.categoryId = :categoryId', { categoryId });
        }

        return query.getMany();
    }

    async findOne(userId: string, tenantId: string, id: string, isAdmin: boolean): Promise<Note> {
        let note;
        if (isAdmin) {
            note = await this.noteRepository.findOne({
                where: { id, tenantId },
                relations: ['user']
            });
        } else {
            note = await this.noteRepository.findOne({
                where: { id, userId, tenantId },
                relations: ['user']
            });
        }

        if (!note) {
            throw new NotFoundException(this.i18nService.formatMessage('noteNotFound', { id }));
        }
        return note;
    }

    async update(userId: string, tenantId: string, id: string, data: Partial<Note>, isAdmin: boolean): Promise<Note> {
        const note = await this.findOne(userId, tenantId, id, isAdmin);
        // Remove protected fields
        delete (data as any).id;
        delete (data as any).userId;
        delete (data as any).createdAt;

        // Handle empty strings for foreign keys
        if (data.groupId === '') {
            data.groupId = null as any;
        }
        if (data.categoryId === '') {
            data.categoryId = null as any;
        }

        Object.assign(note, data);
        return this.noteRepository.save(note);
    }

    async createFromPDFSelection(
        userId: string,
        tenantId: string,
        fileId: string,
        screenshot: Express.Multer.File,
        groupId?: string,
        pageNumber?: number,
    ): Promise<Note> {
        // If groupId is provided, verify that the group exists
        // We'll directly query the group to ensure it exists, regardless of user permissions
        // Since all groups are accessible to all users anyway
        if (groupId) {
            const groupRepo = this.noteRepository.manager.getRepository(KnowledgeGroup);
            const group = await groupRepo.findOne({
                where: { id: groupId, tenantId }
            });

            if (!group) {
                throw new NotFoundException(this.i18nService.formatMessage('knowledgeGroupNotFound', { id: groupId }));
            }

            // Optional: Add logging to help debug permission issues
            console.log(`User ${userId} attempting to add note to group ${groupId}`);
        }

        // Save screenshot to disk
        await this.ensureScreenshotsDir(tenantId);
        const filename = `${uuidv4()}-${Date.now()}.png`;
        const screenshotPath = path.join(this.getScreenshotsDir(tenantId), filename);
        await fs.writeFile(screenshotPath, screenshot.buffer);

        // Extract text using OCR
        let extractedText = '';
        try {
            extractedText = await this.ocrService.extractTextFromImage(screenshot.buffer);
        } catch (error) {
            console.error('OCR extraction failed:', error);
            // Continue without OCR text if extraction fails
        }

        // Create note with screenshot and extracted text
        const note = this.noteRepository.create({
            userId,
            groupId: groupId || null as any,
            title: this.i18nService.formatMessage('pdfNoteTitle', { date: new Date().toLocaleString() }),
            content: extractedText || this.i18nService.getMessage('noTextExtracted'),
            screenshotPath: `${tenantId}/notes-screenshots/${filename}`,
            sourceFileId: fileId,
            sourcePageNumber: pageNumber,
            tenantId,
        });

        return this.noteRepository.save(note);
    }

    async remove(userId: string, tenantId: string, id: string, isAdmin: boolean): Promise<void> {
        let result;
        if (isAdmin) {
            result = await this.noteRepository.delete({ id, tenantId });
        } else {
            result = await this.noteRepository.delete({ id, userId, tenantId });
        }

        if (result.affected === 0) {
            throw new NotFoundException(this.i18nService.formatMessage('noteNotFound', { id }));
        }
    }
}
