import { BadRequestException, Injectable, NotFoundException } from '@nestjs/common';

import * as bcrypt from 'bcryptjs';

import { SessionStatus, UserStatus } from '../generated/prisma/client';

import { PrismaService } from '../infra/prisma.service';



interface UpdateProfileCommand {

  userId: string;

  firstName?: string;

  lastName?: string;

  bio?: string;

  age?: number;

  gender?: string;

}



interface UpdateContactsCommand {

  userId: string;

  email?: string;

  phone?: string;

  backupEmail?: string;

  backupPhone?: string;

}



export function buildDeletedFieldValue(userId: string, value: string): string {

  const prefix = `is_deleted_${userId}_`;

  if (value.startsWith(prefix)) return value;

  return `${prefix}${value}`;

}



function prefixNullable(userId: string, value: string | null | undefined): string | null {

  if (!value) return null;

  return buildDeletedFieldValue(userId, value);

}



@Injectable()

export class ProfileService {

  constructor(private readonly prisma: PrismaService) {}



  async getProfile(userId: string) {

    const user = await this.prisma.user.findUnique({

      where: { id: userId },

      include: { profile: true }

    });

    if (!user) {

      throw new NotFoundException('Пользователь не найден');

    }

    return this.toResponse(user);

  }



  async updateAvatar(userId: string, avatarUrl?: string, avatarStorageKey?: string) {

    if (!avatarStorageKey && !avatarUrl) {

      throw new BadRequestException('Укажите ключ хранилища или ссылку на аватар');

    }



    if (avatarStorageKey) {

      if (!avatarStorageKey.startsWith(`avatars/${userId}/`)) {

        throw new BadRequestException('Некорректный ключ аватара');

      }

      const user = await this.prisma.user.update({

        where: { id: userId },

        data: { avatarStorageKey, avatarUrl: null },

        include: { profile: true }

      });

      return this.toResponse(user);

    }



    if (!avatarUrl || !/^https?:\/\/.+/i.test(avatarUrl)) {

      throw new BadRequestException('Укажите корректную ссылку на аватар или ключ хранилища');

    }

    const user = await this.prisma.user.update({

      where: { id: userId },

      data: { avatarUrl, avatarStorageKey: null },

      include: { profile: true }

    });

    return this.toResponse(user);

  }



  async updateProfile(command: UpdateProfileCommand) {

    const displayName = [command.firstName, command.lastName].filter(Boolean).join(' ').trim();

    const user = await this.prisma.user.update({

      where: { id: command.userId },

      data: {

        displayName: displayName || undefined,

        gender: command.gender,

        profile: {

          upsert: {

            create: {

              firstName: command.firstName,

              lastName: command.lastName,

              bio: command.bio,

              age: command.age,

              gender: command.gender

            },

            update: {

              firstName: command.firstName,

              lastName: command.lastName,

              bio: command.bio,

              age: command.age,

              gender: command.gender

            }

          }

        }

      },

      include: { profile: true }

    });

    return this.toResponse(user);

  }



  async updateContacts(command: UpdateContactsCommand) {

    const user = await this.prisma.user.update({

      where: { id: command.userId },

      data: {

        email: command.email,

        phone: command.phone,

        backupEmail: command.backupEmail,

        backupPhone: command.backupPhone

      },

      include: { profile: true }

    });

    return this.toResponse(user);

  }



  async setPassword(userId: string, password: string) {

    if (!password || password.length < 8) {

      throw new BadRequestException('Пароль должен содержать минимум 8 символов');

    }

    const user = await this.prisma.user.update({

      where: { id: userId },

      data: { passwordHash: await bcrypt.hash(password, 12) }

    });

    return { hasPassword: Boolean(user.passwordHash) };

  }



  async softDeleteProfile(userId: string) {

    const user = await this.prisma.user.findUnique({

      where: { id: userId },

      include: { profile: true, linkedAccounts: true }

    });



    if (!user) {

      throw new NotFoundException('Пользователь не найден');

    }



    if (user.status === UserStatus.DELETED || user.deletedAt) {

      throw new BadRequestException('Профиль уже удалён');

    }



    await this.prisma.$transaction(async (tx) => {

      await tx.session.updateMany({

        where: { userId, status: { in: [SessionStatus.ACTIVE, SessionStatus.LOCKED] } },

        data: { status: SessionStatus.REVOKED }

      });



      await tx.userRole.deleteMany({ where: { userId } });



      await tx.user.update({

        where: { id: userId },

        data: {

          email: prefixNullable(userId, user.email),

          phone: prefixNullable(userId, user.phone),

          backupEmail: prefixNullable(userId, user.backupEmail),

          backupPhone: prefixNullable(userId, user.backupPhone),

          username: prefixNullable(userId, user.username),

          displayName: buildDeletedFieldValue(userId, user.displayName),

          avatarUrl: null,

          avatarStorageKey: null,

          passwordHash: null,

          isSuperAdmin: false,

          status: UserStatus.DELETED,

          deletedAt: new Date()

        }

      });



      if (user.profile) {

        await tx.userProfile.update({

          where: { userId },

          data: {

            firstName: prefixNullable(userId, user.profile.firstName),

            lastName: prefixNullable(userId, user.profile.lastName),

            bio: prefixNullable(userId, user.profile.bio)

          }

        });

      }



      for (const account of user.linkedAccounts) {

        await tx.linkedAccount.update({

          where: { id: account.id },

          data: {

            providerId: buildDeletedFieldValue(userId, account.providerId),

            email: prefixNullable(userId, account.email),

            displayName: account.displayName ? prefixNullable(userId, account.displayName) : null

          }

        });

      }

    });



    return { success: true };

  }



  private toResponse(user: Awaited<ReturnType<PrismaService['user']['findUnique']>> & { profile?: { id?: string; firstName?: string | null; lastName?: string | null; bio?: string | null; age?: number | null; gender?: string | null } | null }) {

    if (!user) {

      throw new NotFoundException('Пользователь не найден');

    }

    return {

      id: user.profile?.id ?? user.id,

      userId: user.id,

      displayName: user.displayName,

      avatarUrl: user.avatarUrl,

      hasAvatar: Boolean(user.avatarStorageKey || user.avatarUrl),

      firstName: user.profile?.firstName,

      lastName: user.profile?.lastName,

      bio: user.profile?.bio,

      age: user.profile?.age,

      gender: user.profile?.gender ?? user.gender,

      email: user.email,

      phone: user.phone,

      backupEmail: user.backupEmail,

      backupPhone: user.backupPhone

    };

  }

}


