import type { GroupWithMembers, IGroupRepository } from '../repositories/group.repository.js';
import type { IUserRepository } from '../repositories/user.repository.js';
import { CreateGroupSchema, UpdateGroupSchema } from '../validation/group.schema.js';
import { NotFoundError, ConflictError } from './mcp-server.service.js';

export class GroupService {
  constructor(
    private readonly groupRepo: IGroupRepository,
    private readonly userRepo: IUserRepository,
  ) {}

  async list(): Promise<GroupWithMembers[]> {
    return this.groupRepo.findAll();
  }

  async getById(id: string): Promise<GroupWithMembers> {
    const group = await this.groupRepo.findById(id);
    if (group === null) {
      throw new NotFoundError(`Group not found: ${id}`);
    }
    return group;
  }

  async getByName(name: string): Promise<GroupWithMembers> {
    const group = await this.groupRepo.findByName(name);
    if (group === null) {
      throw new NotFoundError(`Group not found: ${name}`);
    }
    return group;
  }

  async create(input: unknown): Promise<GroupWithMembers> {
    const data = CreateGroupSchema.parse(input);

    const existing = await this.groupRepo.findByName(data.name);
    if (existing !== null) {
      throw new ConflictError(`Group already exists: ${data.name}`);
    }

    const group = await this.groupRepo.create({
      name: data.name,
      description: data.description,
    });

    if (data.members.length > 0) {
      const userIds = await this.resolveEmails(data.members);
      await this.groupRepo.setMembers(group.id, userIds);
    }

    const result = await this.groupRepo.findById(group.id);
    // Should always exist since we just created it
    return result!;
  }

  async update(id: string, input: unknown): Promise<GroupWithMembers> {
    const data = UpdateGroupSchema.parse(input);

    // Verify exists
    await this.getById(id);

    if (data.description !== undefined) {
      await this.groupRepo.update(id, { description: data.description });
    }

    if (data.members !== undefined) {
      const userIds = await this.resolveEmails(data.members);
      await this.groupRepo.setMembers(id, userIds);
    }

    return this.getById(id);
  }

  async delete(id: string): Promise<void> {
    await this.getById(id);
    await this.groupRepo.delete(id);
  }

  private async resolveEmails(emails: string[]): Promise<string[]> {
    const userIds: string[] = [];
    for (const email of emails) {
      const user = await this.userRepo.findByEmail(email);
      if (user === null) {
        throw new NotFoundError(`User not found: ${email}`);
      }
      userIds.push(user.id);
    }
    return userIds;
  }

  // ── Backup/restore helpers ──

  async upsertByName(data: Record<string, unknown>): Promise<GroupWithMembers> {
    const name = data['name'] as string;
    const members = (data['members'] ?? []) as string[];
    const existing = await this.groupRepo.findByName(name);

    if (existing !== null) {
      if (data['description'] !== undefined) {
        await this.groupRepo.update(existing.id, { description: data['description'] as string });
      }
      if (members.length > 0) {
        const userIds = await this.resolveEmails(members);
        await this.groupRepo.setMembers(existing.id, userIds);
      }
      return this.getById(existing.id);
    }

    const createData: { name: string; description?: string } = { name };
    if (data['description'] !== undefined) createData.description = data['description'] as string;
    const group = await this.groupRepo.create(createData);

    if (members.length > 0) {
      const userIds = await this.resolveEmails(members);
      await this.groupRepo.setMembers(group.id, userIds);
    }

    const result = await this.groupRepo.findById(group.id);
    return result!;
  }

  async deleteByName(name: string): Promise<void> {
    const existing = await this.groupRepo.findByName(name);
    if (existing === null) return;
    await this.groupRepo.delete(existing.id);
  }
}
