import { prisma } from '../config/database';
import { config } from '../config';
import { logger } from '../config/logger';
import { UnauthorizedError, ForbiddenError, NotFoundError } from '../middleware/errorHandler';
import jwt from 'jsonwebtoken';
import {
    ListAnnouncementResponse,
    CreateAnnouncementInput,
    CreateAnnouncementResponse,
    ListAnnouncementInput,
    UpdateAnnouncementInput,
    UpdateAnnouncementResponse,
    DeleteAnnouncementAttachmentInput,
    DeleteAnnouncementAttachmentResponse,
    UploadAnnouncementAttachmentInput,
    UploadAnnouncementAttachmentResponse,
    DeleteAnnouncementInput,
    DeleteAnnouncementResponse,
    Announcement,
} from '../types/announcements.types';
import { CoursesInstructorService } from './CoursesInstructor.service';
import { uploadFile, deleteFile, getPresignedUrl } from '../config/minio';

export class AnnouncementsService {

    /**
     * ดึงรายการประกาศของคอร์ส (ใช้ได้ทั้ง instructor, admin, student)
     * List announcements for a course (accessible by instructor, admin, student)
     */
    async listAnnouncement(input: ListAnnouncementInput): Promise<ListAnnouncementResponse> {
        try {
            const { token, course_id, page = 1, limit = 10 } = input;
            const decoded = jwt.verify(token, config.jwt.secret) as { id: number; type: string };

            // Check user access - instructor, admin, or enrolled student
            const user = await prisma.user.findUnique({
                where: { id: decoded.id },
                include: { role: true },
            });
            if (!user) {
                throw new UnauthorizedError('Invalid token');
            }

            // Admin can access all courses
            const isAdmin = user.role.code === 'ADMIN';

            // Check if instructor of this course
            const isInstructor = await prisma.courseInstructor.findFirst({
                where: { course_id, user_id: decoded.id },
            });

            // Check if enrolled student
            const isEnrolled = await prisma.enrollment.findFirst({
                where: { course_id, user_id: decoded.id },
            });

            if (!isAdmin && !isInstructor && !isEnrolled) throw new ForbiddenError('You do not have access to this course announcements');

            const skip = (page - 1) * limit;

            // Students only see PUBLISHED announcements with published_at <= now
            const now = new Date();
            const whereClause: any = { course_id };
            
            if (!(isAdmin || isInstructor)) {
                // Students: only show PUBLISHED and published_at <= now
                whereClause.status = 'PUBLISHED';
                whereClause.published_at = { lte: now };
            }

            const [announcements, total] = await Promise.all([
                prisma.announcement.findMany({
                    where: whereClause,
                    include: {
                        attachments: true,
                    },
                    orderBy: [
                        { is_pinned: 'desc' },
                        { created_at: 'desc' },
                    ],
                    skip,
                    take: limit,
                }),
                prisma.announcement.count({ where: whereClause }),
            ]);

            // Generate presigned URLs for attachments
            const announcementsWithUrls = await Promise.all(
                announcements.map(async (a) => {
                    const attachmentsWithUrls = await Promise.all(
                        a.attachments.map(async (att) => {
                            let presigned_url: string | null = null;
                            try {
                                presigned_url = await getPresignedUrl(att.file_path, 3600);
                            } catch (err) {
                                logger.warn(`Failed to generate presigned URL for ${att.file_path}: ${err}`);
                            }
                            return {
                                id: att.id,
                                announcement_id: att.announcement_id,
                                file_name: att.file_name,
                                file_path: att.file_path,
                                presigned_url,
                                created_at: att.created_at,
                                updated_at: att.created_at,
                            };
                        })
                    );
                    return {
                        id: a.id,
                        title: a.title as { th: string; en: string },
                        content: a.content as { th: string; en: string },
                        status: a.status,
                        is_pinned: a.is_pinned,
                        published_at: a.published_at,
                        created_at: a.created_at,
                        updated_at: a.updated_at,
                        attachments: attachmentsWithUrls,
                    };
                })
            );

            return {
                code: 200,
                message: 'Announcements retrieved successfully',
                data: announcementsWithUrls,
                total,
                page,
                limit,
            };
        } catch (error) {
            logger.error(`Error listing announcements: ${error}`);
            throw error;
        }
    }

    /**
     * สร้างประกาศใหม่ (พร้อมอัปโหลดไฟล์แนบ)
     * Create a new announcement with optional file attachments
     */
    async createAnnouncement(input: CreateAnnouncementInput): Promise<CreateAnnouncementResponse> {
        try {
            const { token, course_id, title, content, status, is_pinned, published_at, files } = input;
            const decoded = jwt.verify(token, config.jwt.secret) as { id: number };

            // Validate instructor access
            await CoursesInstructorService.validateCourseInstructor(token, course_id);

            // Determine published_at: use provided value or default to now if status is PUBLISHED
            let finalPublishedAt: Date | null = null;
            if (status === 'PUBLISHED') {
                finalPublishedAt = published_at ? new Date(published_at) : new Date();
            }

            // Create announcement
            const announcement = await prisma.announcement.create({
                data: {
                    course_id,
                    title,
                    content,
                    status: status as any,
                    is_pinned,
                    published_at: finalPublishedAt,
                    created_by: decoded.id,
                },
            });

            // Upload attachments if provided
            const attachments: {
                id: number;
                announcement_id: number;
                file_name: string;
                file_path: string;
                created_at: Date;
                updated_at: Date;
            }[] = [];

            if (files && files.length > 0) {
                for (const file of files) {
                    const timestamp = Date.now();
                    const uniqueId = Math.random().toString(36).substring(2, 15);
                    const fileName = file.originalname || 'file';
                    const extension = fileName.split('.').pop() || '';
                    const safeFilename = `${timestamp}-${uniqueId}.${extension}`;
                    const filePath = `courses/${course_id}/announcements/${announcement.id}/${safeFilename}`;

                    // Upload to MinIO
                    await uploadFile(filePath, file.buffer, file.mimetype || 'application/octet-stream');

                    // Create attachment record
                    const attachment = await prisma.announcementAttachment.create({
                        data: {
                            announcement_id: announcement.id,
                            file_name: fileName,
                            file_path: filePath,
                            file_size: file.size,
                            mime_type: file.mimetype || 'application/octet-stream',
                        },
                    });

                    attachments.push({
                        id: attachment.id,
                        announcement_id: attachment.announcement_id,
                        file_name: attachment.file_name,
                        file_path: attachment.file_path,
                        created_at: attachment.created_at,
                        updated_at: attachment.created_at,
                    });
                }
            }

            return {
                code: 201,
                message: 'Announcement created successfully',
                data: {
                    id: announcement.id,
                    title: announcement.title as { th: string; en: string },
                    content: announcement.content as { th: string; en: string },
                    status: announcement.status,
                    is_pinned: announcement.is_pinned,
                    published_at: announcement.published_at,
                    created_at: announcement.created_at,
                    updated_at: announcement.updated_at,
                    attachments,
                },
            };
        } catch (error) {
            logger.error(`Error creating announcement: ${error}`);
            throw error;
        }
    }

    /**
     * อัปเดตประกาศ
     * Update an announcement
     */
    async updateAnnouncement(input: UpdateAnnouncementInput): Promise<UpdateAnnouncementResponse> {
        try {
            const { token, course_id, announcement_id, title, content, status, is_pinned, published_at } = input;
            const decoded = jwt.verify(token, config.jwt.secret) as { id: number };

            // Validate instructor access
            await CoursesInstructorService.validateCourseInstructor(token, course_id);

            // Check announcement exists and belongs to course
            const existing = await prisma.announcement.findFirst({
                where: { id: announcement_id, course_id },
            });

            if (!existing) {
                throw new NotFoundError('Announcement not found');
            }

            // Determine published_at
            let finalPublishedAt: Date | null = existing.published_at;
            if (status === 'PUBLISHED') {
                if (published_at) {
                    finalPublishedAt = new Date(published_at);
                } else if (existing.status !== 'PUBLISHED') {
                    finalPublishedAt = new Date();
                }
            }

            const announcement = await prisma.announcement.update({
                where: { id: announcement_id },
                data: {
                    title,
                    content,
                    status: status as any,
                    is_pinned,
                    published_at: finalPublishedAt,
                    updated_by: decoded.id,
                },
                include: {
                    attachments: true,
                },
            });

            return {
                code: 200,
                message: 'Announcement updated successfully',
                data: {
                    id: announcement.id,
                    title: announcement.title as { th: string; en: string },
                    content: announcement.content as { th: string; en: string },
                    status: announcement.status,
                    is_pinned: announcement.is_pinned,
                    published_at: announcement.published_at,
                    created_at: announcement.created_at,
                    updated_at: announcement.updated_at,
                    attachments: announcement.attachments.map(att => ({
                        id: att.id,
                        announcement_id: att.announcement_id,
                        file_name: att.file_name,
                        file_path: att.file_path,
                        created_at: att.created_at,
                        updated_at: att.created_at,
                    })),
                },
            };
        } catch (error) {
            logger.error(`Error updating announcement: ${error}`);
            throw error;
        }
    }

    /**
     * ลบประกาศ
     * Delete an announcement
     */
    async deleteAnnouncement(input: DeleteAnnouncementInput): Promise<DeleteAnnouncementResponse> {
        try {
            const { token, course_id, announcement_id } = input;
            jwt.verify(token, config.jwt.secret) as { id: number };

            // Validate instructor access
            await CoursesInstructorService.validateCourseInstructor(token, course_id);

            // Check announcement exists and belongs to course
            const existing = await prisma.announcement.findFirst({
                where: { id: announcement_id, course_id },
                include: { attachments: true },
            });

            if (!existing) {
                throw new NotFoundError('Announcement not found');
            }

            // Delete attachments from storage
            for (const attachment of existing.attachments) {
                try {
                    await deleteFile(attachment.file_path);
                } catch (err) {
                    logger.warn(`Failed to delete attachment file: ${attachment.file_path}`);
                }
            }

            // Delete announcement (attachments cascade deleted)
            await prisma.announcement.delete({
                where: { id: announcement_id },
            });

            return {
                code: 200,
                message: 'Announcement deleted successfully',
            };
        } catch (error) {
            logger.error(`Error deleting announcement: ${error}`);
            throw error;
        }
    }

    /**
     * อัปโหลดไฟล์แนบ
     * Upload attachment to announcement
     */
    async uploadAttachment(input: UploadAnnouncementAttachmentInput): Promise<UploadAnnouncementAttachmentResponse> {
        try {
            const { token, course_id, announcement_id, file } = input;
            jwt.verify(token, config.jwt.secret) as { id: number };

            // Validate instructor access
            await CoursesInstructorService.validateCourseInstructor(token, course_id);

            // Check announcement exists and belongs to course
            const existing = await prisma.announcement.findFirst({
                where: { id: announcement_id, course_id },
            });

            if (!existing) {
                throw new NotFoundError('Announcement not found');
            }

            // Generate file path
            const timestamp = Date.now();
            const uniqueId = Math.random().toString(36).substring(2, 15);
            const fileName = (file as any).originalname || 'file';
            const extension = fileName.split('.').pop() || '';
            const safeFilename = `${timestamp}-${uniqueId}.${extension}`;
            const filePath = `courses/${course_id}/announcements/${announcement_id}/${safeFilename}`;

            // Upload to MinIO
            const fileBuffer = (file as any).buffer;
            const mimeType = (file as any).mimetype || 'application/octet-stream';
            const fileSize = fileBuffer.length;

            await uploadFile(filePath, fileBuffer, mimeType);

            // Create attachment record
            const attachment = await prisma.announcementAttachment.create({
                data: {
                    announcement_id,
                    file_name: fileName,
                    file_path: filePath,
                    file_size: fileSize,
                    mime_type: mimeType,
                },
            });

            return {
                code: 201,
                message: 'Attachment uploaded successfully',
                data: {
                    id: attachment.id,
                    announcement_id: attachment.announcement_id,
                    file_name: attachment.file_name,
                    file_path: attachment.file_path,
                    created_at: attachment.created_at,
                    updated_at: attachment.created_at,
                },
            };
        } catch (error) {
            logger.error(`Error uploading attachment: ${error}`);
            throw error;
        }
    }

    /**
     * ลบไฟล์แนบ
     * Delete attachment from announcement
     */
    async deleteAttachment(input: DeleteAnnouncementAttachmentInput): Promise<DeleteAnnouncementAttachmentResponse> {
        try {
            const { token, course_id, announcement_id, attachment_id } = input;
            jwt.verify(token, config.jwt.secret) as { id: number };

            // Validate instructor access
            await CoursesInstructorService.validateCourseInstructor(token, course_id);

            // Check attachment exists and belongs to announcement in this course
            const attachment = await prisma.announcementAttachment.findFirst({
                where: {
                    id: attachment_id,
                    announcement_id,
                    announcement: { course_id },
                },
            });

            if (!attachment) {
                throw new NotFoundError('Attachment not found');
            }

            // Delete from storage
            try {
                await deleteFile(attachment.file_path);
            } catch (err) {
                logger.warn(`Failed to delete attachment file: ${attachment.file_path}`);
            }

            // Delete record
            await prisma.announcementAttachment.delete({
                where: { id: attachment_id },
            });

            return {
                code: 200,
                message: 'Attachment deleted successfully',
            };
        } catch (error) {
            logger.error(`Error deleting attachment: ${error}`);
            throw error;
        }
    }
}