import { Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service.js';
import { StorageService } from '../storage/storage.service.js';
import { SmbService } from '../storage/smb.service.js';
import { NodeType } from '@prisma/client';
import { v4 as uuidv4 } from 'uuid';

@Injectable()
export class NodesService {
  constructor(
    private readonly prisma: PrismaService,
    private readonly storageService: StorageService,
    private readonly smbService: SmbService
  ) {}

  async createFolder(name: string, parentId: string | null, userId: string) {
    const parentPath = parentId ? Buffer.from(parentId, 'base64').toString('utf8') : '';
    const targetPath = parentPath ? `${parentPath}\\${name}` : name;
    
    await this.smbService.mkdir(userId, targetPath);
    
    return {
      id: Buffer.from(targetPath).toString('base64'),
      parentId,
      name,
      type: 'FOLDER',
      sizeBytes: 0,
      mimeType: 'folder',
      createdAt: new Date(),
      updatedAt: new Date(),
      ownerId: userId
    };
  }

  async requestFileUpload(name: string, sizeBytes: number, parentId: string | null, mimeType: string, userId: string) {
    // Karena kita memakai SMB Proxy, parentId adalah base64 string dari direktori target
    const parentPath = parentId ? Buffer.from(parentId, 'base64').toString('utf8') : '';
    const targetPath = parentPath ? `${parentPath}\\${name}` : name;
    
    const uploadId = Buffer.from(targetPath).toString('base64');
    const backendUrl = process.env.BACKEND_URL || 'http://localhost:3000';
    const uploadUrl = `${backendUrl}/storage/upload/${encodeURIComponent(uploadId)}`;
    
    // Kirim node "palsu" ke UI agar bisa langsung tampil (UI tidak butuh record database)
    const node = {
      id: uploadId,
      parentId,
      name,
      type: 'FILE',
      sizeBytes,
      mimeType,
      createdAt: new Date(),
      updatedAt: new Date(),
    };
    
    return { node, uploadUrl };
  }

  async getFileDownloadUrl(nodeId: string, userId: string) {
    // Karena kita memakai SMB Proxy, nodeId adalah base64 string dari file path
    const backendUrl = process.env.BACKEND_URL || 'http://localhost:3000';
    const downloadUrl = `${backendUrl}/storage/download/${encodeURIComponent(nodeId)}`;
    return { downloadUrl };
  }

  async listNodes(parentId: string | null, userId: string, tab: string = 'MY_DRIVE', search?: string) {
    if (tab === 'MY_DRIVE') {
      try {
        // Jika parentId null, berarti kita di halaman depan Syafana Drive!
        // Tampilkan daftar Share sebagai "Folder"
        if (!parentId) {
          const sharesEnv = process.env.SMB_EXPOSED_SHARES || 'IT,HRD_SMT1 20242025,Finance & Accounting,Users,Shared Folders';
          const shares = sharesEnv.split(',').map(s => s.trim());
          
          // Uji koneksi secara paralel ke setiap share (On-The-Fly Permission Check!)
          const accessResults = await Promise.all(
            shares.map(async (shareName) => {
              const hasAccess = await this.smbService.testShareAccess(userId, shareName);
              return { shareName, hasAccess };
            })
          );
          
          // Hanya ambil share yang user berhak masuk
          const allowedShares = accessResults.filter(r => r.hasAccess).map(r => r.shareName);
          
          let nodes = allowedShares.map(shareName => ({
            id: Buffer.from(shareName).toString('base64'),
            parentId: null,
            name: shareName,
            type: 'FOLDER',
            sizeBytes: 0,
            mimeType: 'folder',
            createdAt: new Date(),
            updatedAt: new Date(),
            ownerId: userId,
            isStarred: false,
          }));
          
          if (search) {
            nodes = nodes.filter(n => n.name.toLowerCase().includes(search.toLowerCase()));
          }
          return nodes;
        }

        // Jika bukan root, berarti sedang melihat isi dari salah satu Share
        const fullPath = Buffer.from(parentId, 'base64').toString('utf8');
        const items = await this.smbService.readdirWithStats(userId, fullPath);
        
        let nodes = items.map(item => {
          let mimeType = item.isDirectory ? 'folder' : 'application/octet-stream';
          if (!item.isDirectory) {
            const ext = item.name.split('.').pop()?.toLowerCase();
            if (ext === 'pdf') mimeType = 'application/pdf';
            else if (['jpg', 'jpeg', 'png', 'gif'].includes(ext)) mimeType = `image/${ext}`;
            else if (['mp4', 'mkv'].includes(ext)) mimeType = `video/${ext}`;
            else if (['txt'].includes(ext)) mimeType = 'text/plain';
          }

          return {
            // ID adalah path lengkap dari Share sampai file, misal "IT\Folder\file.txt"
            id: Buffer.from(item.path).toString('base64'),
            parentId,
            name: item.name,
            type: item.isDirectory ? 'FOLDER' : 'FILE',
            sizeBytes: item.size,
            mimeType,
            createdAt: item.mtime,
            updatedAt: item.mtime,
            ownerId: userId,
            isStarred: false,
          };
        });

        if (search) {
          nodes = nodes.filter(n => n.name.toLowerCase().includes(search.toLowerCase()));
        }
        return nodes;
      } catch (e) {
        console.error("SMB List Error:", e);
        throw new Error("Gagal membaca folder jaringan SMB. Pastikan sesi Anda aktif dan Anda memiliki hak akses.");
      }
    }
    return [];
  }

  async getPermissions(nodeId: string, userId: string) {
    const node = await this.prisma.node.findFirst({ where: { id: nodeId, ownerId: userId } });
    if (!node) throw new Error("Permission denied");
    return this.prisma.nodePermission.findMany({
      where: { nodeId },
      include: { user: { select: { email: true } } }
    });
  }

  async updatePermission(nodeId: string, permissionId: string, role: any, userId: string) {
    const node = await this.prisma.node.findFirst({ where: { id: nodeId, ownerId: userId } });
    if (!node) throw new Error("Permission denied");
    return this.prisma.nodePermission.update({
      where: { id: permissionId },
      data: { role }
    });
  }

  async removePermission(nodeId: string, permissionId: string, userId: string) {
    const node = await this.prisma.node.findFirst({ where: { id: nodeId, ownerId: userId } });
    if (!node) throw new Error("Permission denied");
    return this.prisma.nodePermission.delete({
      where: { id: permissionId }
    });
  }

  async renameNode(id: string, name: string, userId: string) {
    const oldPath = Buffer.from(id, 'base64').toString('utf8');
    const folderPath = oldPath.includes('\\') ? oldPath.substring(0, oldPath.lastIndexOf('\\')) : '';
    const newPath = folderPath ? `${folderPath}\\${name}` : name;
    
    await this.smbService.rename(userId, oldPath, newPath);
    return { success: true };
  }

  async deleteNode(id: string, userId: string) {
    const targetPath = Buffer.from(id, 'base64').toString('utf8');
    const stat = await this.smbService.stat(userId, targetPath);
    
    if (stat.isDirectory()) {
      await this.smbService.rmdir(userId, targetPath);
    } else {
      await this.smbService.unlink(userId, targetPath);
    }
    return { success: true };
  }

  async restoreNode(id: string, userId: string) {
    return this.prisma.node.updateMany({
      where: { id, ownerId: userId },
      data: { isDeleted: false }
    });
  }

  async shareNode(id: string, email: string, userId: string) {
    const node = await this.prisma.node.findFirst({ where: { id, ownerId: userId }});
    if (!node) throw new Error("Node not found");

    const targetUser = await this.prisma.user.findUnique({ where: { email }});
    if (!targetUser) throw new Error("User with that email not found");
    if (targetUser.id === userId) throw new Error("Cannot share with yourself");

    await this.prisma.$executeRawUnsafe(`
      INSERT INTO Notification (id, userId, title, message, isRead, createdAt)
      VALUES (UUID(), '${targetUser.id}', 'Berbagi', 'Sebuah file/folder telah dibagikan kepada Anda', 0, NOW(3))
    `);

    const existing = await this.prisma.nodePermission.findFirst({
      where: {
        nodeId: id,
        granteeType: 'USER',
        granteeId: targetUser.id
      }
    });

    if (existing) {
      return this.prisma.nodePermission.update({
        where: { id: existing.id },
        data: { role: 'VIEWER' }
      });
    } else {
      return this.prisma.nodePermission.create({
        data: {
          nodeId: id,
          granteeType: 'USER',
          granteeId: targetUser.id,
          role: 'VIEWER'
        }
      });
    }
  }

  async moveNode(id: string, newParentId: string | null, userId: string) {
    if (id === newParentId) throw new Error("Cannot move into itself");
    
    const oldPath = Buffer.from(id, 'base64').toString('utf8');
    const fileName = oldPath.includes('\\') ? oldPath.substring(oldPath.lastIndexOf('\\') + 1) : oldPath;
    const parentPath = newParentId ? Buffer.from(newParentId, 'base64').toString('utf8') : '';
    const newPath = parentPath ? `${parentPath}\\${fileName}` : fileName;
    
    await this.smbService.rename(userId, oldPath, newPath);
    return { success: true };
  }

  async hasEditorPermission(nodeId: string, userId: string): Promise<boolean> {
    let currentId: string | null = nodeId;
    while (currentId) {
      const currentNode: any = await this.prisma.node.findUnique({
        where: { id: currentId },
        include: { permissions: { where: { granteeId: userId, granteeType: 'USER' } } }
      });
      if (!currentNode) return false;
      if (currentNode.ownerId === userId) return true;
      
      const perm = currentNode.permissions[0];
      if (perm && (perm.role === 'EDITOR' || (perm.role as any) === 'OWNER')) return true;
      
      currentId = currentNode.parentId;
    }
    return false;
  }

  async getStarredIds(userId: string) {
    const rows = await this.prisma.$queryRawUnsafe<any[]>(`SELECT id FROM Node WHERE ownerId = '${userId}' AND isStarred = 1 AND isDeleted = 0`);
    return rows.map(r => r.id);
  }

  async toggleStar(id: string, isStarred: boolean, userId: string) {
    // Gunakan executeRaw karena Prisma Client tidak bisa diregenerasi saat server menyala
    await this.prisma.$executeRaw`UPDATE Node SET isStarred = ${isStarred ? 1 : 0} WHERE id = ${id} AND ownerId = ${userId}`;
    return { success: true };
  }

  async updateFolderColor(id: string, color: string, userId: string) {
    const canEdit = await this.hasEditorPermission(id, userId);
    if (!canEdit) throw new Error("Permission denied");
    return this.prisma.node.updateMany({
      where: { id, type: 'FOLDER' },
      data: { mimeType: color }
    });
  }

  async getNotifications(userId: string) {
    return this.prisma.$queryRawUnsafe(`SELECT * FROM Notification WHERE userId = '${userId}' ORDER BY createdAt DESC LIMIT 10`);
  }

  async markNotificationsRead(userId: string) {
    await this.prisma.$executeRawUnsafe(`UPDATE Notification SET isRead = 1 WHERE userId = '${userId}' AND isRead = 0`);
    return { success: true };
  }
}
