import prisma from '../utils/db';
import crypto from 'crypto';
import { MailService } from './mail.service';
import { s3Client, BUCKET_NAME } from '../utils/s3';
import { DeleteObjectCommand } from '@aws-sdk/client-s3';

export class AssetService {
  private mailService = new MailService();

  public async triggerOnboardingForEligiblePartners(orgIds: string[], userIds: string[]) {
    // Find all users in PENDING_ASSETS state who belong to either the userIds list or orgIds list
    const users = await prisma.user.findMany({
      where: {
        onboardingStatus: 'PENDING_ASSETS',
        OR: [
          { id: { in: userIds } },
          { organizationId: { in: orgIds } }
        ]
      }
    });

    for (const user of users) {
      // Find count of shared assets for this user (either user-specific or organization-level)
      const count = await prisma.sharedAsset.count({
        where: {
          OR: [
            { userId: user.id },
            { organizationId: user.organizationId || undefined, userId: null }
          ]
        }
      });

      if (count > 0) {
        let inviteToken = user.inviteToken;
        let inviteTokenExp = user.inviteTokenExp;

        if (!inviteToken) {
          inviteToken = crypto.randomBytes(32).toString('hex');
          inviteTokenExp = new Date(Date.now() + 24 * 60 * 60 * 1000);
        }

        await prisma.user.update({
          where: { id: user.id },
          data: {
            onboardingStatus: 'PENDING_ONBOARDING',
            inviteToken,
            inviteTokenExp,
          }
        });

        // Trigger the SMTP email
        await this.mailService.sendInviteEmail(user.email, inviteToken!);
      }
    }
  }

  public async createAsset(data: any) {
    const { sharedOrgIds, shares, tags, ...rest } = data;
    
    // Parse tags
    let parsedTags: string[] = [];
    if (Array.isArray(tags)) {
      parsedTags = tags;
    } else if (typeof tags === 'string' && tags.trim()) {
      try {
        parsedTags = JSON.parse(tags);
      } catch {
        parsedTags = tags.split(',').map((t: string) => t.trim()).filter(Boolean);
      }
    }

    const asset = await prisma.asset.create({
      data: {
        ...rest,
        tags: parsedTags,
      }
    });

    // Handle immediate sharing
    const orgIdsToCheck: string[] = [];
    const userIdsToCheck: string[] = [];

    if (shares) {
      let parsedShares: any[] = [];
      if (Array.isArray(shares)) {
        parsedShares = shares;
      } else if (typeof shares === 'string' && shares.trim()) {
        try {
          parsedShares = JSON.parse(shares);
        } catch {
          parsedShares = shares.split(',').map((id: string) => ({ organizationId: id.trim(), userId: null })).filter(s => s.organizationId);
        }
      }

      if (parsedShares.length > 0) {
        await prisma.sharedAsset.createMany({
          data: parsedShares.map(s => ({
            assetId: asset.id,
            organizationId: s.organizationId,
            userId: s.userId || null,
          })),
          skipDuplicates: true,
        });
        parsedShares.forEach(s => {
          if (s.organizationId) orgIdsToCheck.push(s.organizationId);
          if (s.userId) userIdsToCheck.push(s.userId);
        });
      }
    } else if (sharedOrgIds) {
      let orgIds: string[] = [];
      if (Array.isArray(sharedOrgIds)) {
        orgIds = sharedOrgIds;
      } else if (typeof sharedOrgIds === 'string' && sharedOrgIds.trim()) {
        try {
          orgIds = JSON.parse(sharedOrgIds);
        } catch {
          orgIds = sharedOrgIds.split(',').map((id: string) => id.trim()).filter(Boolean);
        }
      }

      if (orgIds.length > 0) {
        await prisma.sharedAsset.createMany({
          data: orgIds.map(orgId => ({
            assetId: asset.id,
            organizationId: orgId,
            userId: null,
          })),
          skipDuplicates: true,
        });
        orgIdsToCheck.push(...orgIds);
      }
    }

    if (orgIdsToCheck.length > 0 || userIdsToCheck.length > 0) {
      await this.triggerOnboardingForEligiblePartners(orgIdsToCheck, userIdsToCheck);
    }

    return this.getAssetById(asset.id);
  }

  public async getAssets(userContext?: { role: string; userId: string }) {
    if (!userContext) {
      return [];
    }

    if (userContext.role === 'ADMIN') {
      return await prisma.asset.findMany({
        include: {
          sharedWith: {
            include: {
              organization: {
                select: { id: true, name: true }
              },
              user: {
                select: { id: true, email: true }
              }
            }
          },
          downloadRequests: {
            include: {
              user: {
                select: { id: true, email: true }
              }
            }
          }
        },
        orderBy: { createdAt: 'desc' }
      });
    }

    // For clients/partners, find user organization first
    const user = await prisma.user.findUnique({
      where: { id: userContext.userId }
    });

    if (!user || !user.organizationId) {
      return [];
    }

    // Support comma-separated multiple groups
    const userGroups = user.partnerGroup
      ? user.partnerGroup.split(',').map(s => s.trim().toLowerCase())
      : [];

    const whereClause: any = {
      status: 'published',
      OR: [
        {
          sharedWith: {
            some: {
              organizationId: user.organizationId,
              OR: [
                { userId: null },
                { userId: user.id }
              ]
            }
          }
        }
      ]
    };

    if (userGroups.length > 0) {
      whereClause.OR.push({
        assetGroups: {
          some: {
            name: {
              in: userGroups,
              mode: 'insensitive'
            }
          }
        }
      });
    }

    return await prisma.asset.findMany({
      where: whereClause,
      include: {
        sharedWith: {
          include: {
            organization: {
              select: { id: true, name: true }
            },
            user: {
              select: { id: true, email: true }
            }
          }
        },
        downloadRequests: {
          where: { userId: userContext.userId }
        }
      },
      orderBy: { createdAt: 'desc' }
    });
  }

  public async getAssetById(id: string) {
    return await prisma.asset.findUnique({
      where: { id },
      include: {
        sharedWith: {
          include: {
            organization: {
              select: { id: true, name: true }
            },
            user: {
              select: { id: true, email: true }
            }
          }
        },
        downloadRequests: {
          include: {
            user: {
              select: { id: true, email: true }
            }
          }
        }
      }
    });
  }

  public async updateAsset(id: string, data: any) {
    const { sharedOrgIds, shares, tags, ...rest } = data;
    
    const updateData: any = { ...rest };

    if (tags !== undefined) {
      let parsedTags: string[] = [];
      if (Array.isArray(tags)) {
        parsedTags = tags;
      } else if (typeof tags === 'string') {
        try {
          parsedTags = JSON.parse(tags);
        } catch {
          parsedTags = tags.split(',').map((t: string) => t.trim()).filter(Boolean);
        }
      }
      updateData.tags = parsedTags;
    }

    await prisma.asset.update({
      where: { id },
      data: updateData
    });

    const orgIdsToCheck: string[] = [];
    const userIdsToCheck: string[] = [];

    if (shares !== undefined) {
      let parsedShares: any[] = [];
      if (Array.isArray(shares)) {
        parsedShares = shares;
      } else if (typeof shares === 'string') {
        try {
          parsedShares = JSON.parse(shares);
        } catch {
          parsedShares = shares.split(',').map((oid: string) => ({ organizationId: oid.trim(), userId: null })).filter(s => s.organizationId);
        }
      }

      await prisma.sharedAsset.deleteMany({ where: { assetId: id } });
      if (parsedShares.length > 0) {
        await prisma.sharedAsset.createMany({
          data: parsedShares.map(s => ({
            assetId: id,
            organizationId: s.organizationId,
            userId: s.userId || null,
          })),
          skipDuplicates: true
        });
        parsedShares.forEach(s => {
          if (s.organizationId) orgIdsToCheck.push(s.organizationId);
          if (s.userId) userIdsToCheck.push(s.userId);
        });
      }
    } else if (sharedOrgIds !== undefined) {
      let orgIds: string[] = [];
      if (Array.isArray(sharedOrgIds)) {
        orgIds = sharedOrgIds;
      } else if (typeof sharedOrgIds === 'string') {
        try {
          orgIds = JSON.parse(sharedOrgIds);
        } catch {
          orgIds = sharedOrgIds.split(',').map((oid: string) => oid.trim()).filter(Boolean);
        }
      }

      await prisma.sharedAsset.deleteMany({ where: { assetId: id } });
      if (orgIds.length > 0) {
        await prisma.sharedAsset.createMany({
          data: orgIds.map(orgId => ({
            assetId: id,
            organizationId: orgId,
            userId: null,
          })),
          skipDuplicates: true
        });
        orgIdsToCheck.push(...orgIds);
      }
    }

    if (orgIdsToCheck.length > 0 || userIdsToCheck.length > 0) {
      await this.triggerOnboardingForEligiblePartners(orgIdsToCheck, userIdsToCheck);
    }

    return this.getAssetById(id);
  }

  public async shareAsset(assetId: string, organizationIds: string[]) {
    await prisma.sharedAsset.createMany({
      data: organizationIds.map(orgId => ({
        assetId,
        organizationId: orgId,
        userId: null,
      })),
      skipDuplicates: true
    });

    await this.triggerOnboardingForEligiblePartners(organizationIds, []);

    return this.getAssetById(assetId);
  }

  public async unshareAsset(assetId: string, organizationIds: string[]) {
    await prisma.sharedAsset.deleteMany({
      where: {
        assetId,
        organizationId: { in: organizationIds },
        userId: null
      }
    });
    return this.getAssetById(assetId);
  }

  public async bulkShareAssets(assetIds: string[], shares: any[]) {
    await prisma.$transaction([
      prisma.sharedAsset.deleteMany({
        where: { assetId: { in: assetIds } }
      }),
      prisma.sharedAsset.createMany({
        data: assetIds.flatMap(assetId => 
          shares.map(s => ({
            assetId,
            organizationId: s.organizationId,
            userId: s.userId || null,
          }))
        ),
        skipDuplicates: true
      })
    ]);

    const orgIds = shares.map(s => s.organizationId).filter(Boolean);
    const userIds = shares.map(s => s.userId).filter(Boolean);
    await this.triggerOnboardingForEligiblePartners(orgIds, userIds);
  }

  public async incrementDownloadCount(id: string) {
    return await prisma.asset.update({
      where: { id },
      data: {
        downloadsCount: { increment: 1 }
      }
    });
  }

  public async deleteAsset(id: string) {
    const asset = await prisma.asset.findUnique({ where: { id } });
    if (asset && asset.url.startsWith('/uploads/')) {
      const filename = asset.url.replace('/uploads/', '');
      try {
        await s3Client.send(new DeleteObjectCommand({
          Bucket: BUCKET_NAME,
          Key: filename,
        }));
        console.log(`[S3] Deleted file: ${filename}`);
      } catch (err) {
        console.error(`[S3] Failed to delete file ${filename}:`, err);
      }
    }
    return await prisma.asset.delete({ where: { id } });
  }

  // Download Requests Access Methods
  public async requestDownload(assetId: string, userId: string) {
    const existing = await prisma.downloadRequest.findFirst({
      where: { assetId, userId }
    });
    if (existing) {
      return await prisma.downloadRequest.update({
        where: { id: existing.id },
        data: { status: 'PENDING' }
      });
    }
    return await prisma.downloadRequest.create({
      data: {
        assetId,
        userId,
        status: 'PENDING'
      }
    });
  }

  public async approveDownloadRequest(requestId: string) {
    return await prisma.downloadRequest.update({
      where: { id: requestId },
      data: { status: 'APPROVED' }
    });
  }

  public async rejectDownloadRequest(requestId: string) {
    return await prisma.downloadRequest.update({
      where: { id: requestId },
      data: { status: 'REJECTED' }
    });
  }

  public async createAssetGroup(name: string, description?: string, assetIds: string[] = []) {
    return await prisma.assetGroup.create({
      data: {
        name,
        description,
        assets: {
          connect: assetIds.map(id => ({ id }))
        }
      },
      include: {
        assets: true
      }
    });
  }

  public async addAssetsToGroup(groupId: string, assetIds: string[]) {
    return await prisma.assetGroup.update({
      where: { id: groupId },
      data: {
        assets: {
          connect: assetIds.map(id => ({ id }))
        }
      },
      include: {
        assets: true
      }
    });
  }

  public async removeAssetsFromGroup(groupId: string, assetIds: string[]) {
    return await prisma.assetGroup.update({
      where: { id: groupId },
      data: {
        assets: {
          disconnect: assetIds.map(id => ({ id }))
        }
      },
      include: {
        assets: true
      }
    });
  }

  public async getAssetGroups() {
    return await prisma.assetGroup.findMany({
      include: {
        assets: true
      },
      orderBy: { createdAt: 'desc' }
    });
  }

  public async deleteAssetGroup(id: string) {
    return await prisma.assetGroup.delete({
      where: { id }
    });
  }
}
