import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { IsNull, Like, Repository } from 'typeorm';
import { Case } from './case.entity';
import { CaseEntry } from '../case-entries/case-entry.entity';
import { CreateCaseDto } from './dto/create-case.dto';
import { UpdateCaseDto } from './dto/update-case.dto';

@Injectable()
export class CasesService {
  constructor(
    @InjectRepository(Case)
    private readonly casesRepo: Repository<Case>,
    @InjectRepository(CaseEntry)
    private readonly entriesRepo: Repository<CaseEntry>,
  ) {}

  create(dto: CreateCaseDto): Promise<Case> {
    return this.casesRepo.save(this.casesRepo.create(dto));
  }

  /** Admin listing: teaching cases authored in the CMS, not learner logs. */
  findAll(search?: string): Promise<Case[]> {
    return this.casesRepo.find({
      where: search
        ? [
            { anonymousSessionId: IsNull(), title: Like(`%${search}%`) },
            { anonymousSessionId: IsNull(), patientCode: Like(`%${search}%`) },
          ]
        : { anonymousSessionId: IsNull() },
      relations: ['entries'],
      order: { createdAt: 'DESC' },
    });
  }

  findPublished(): Promise<Case[]> {
    return this.casesRepo.find({
      where: { status: 'PUBLISHED', anonymousSessionId: IsNull() },
      relations: ['entries'],
      order: { createdAt: 'DESC' },
    });
  }

  findBySession(sessionId: string): Promise<Case[]> {
    return this.casesRepo.find({
      where: { anonymousSessionId: sessionId },
      relations: ['entries'],
      order: { updatedAt: 'DESC' },
    });
  }

  findById(id: number): Promise<Case | null> {
    return this.casesRepo.findOne({ where: { id }, relations: ['entries'] });
  }

  async update(id: number, dto: UpdateCaseDto): Promise<Case | null> {
    await this.casesRepo.update(id, dto);
    return this.findById(id);
  }

  async setStatus(id: number, status: string): Promise<Case | null> {
    await this.casesRepo.update(id, { status });
    return this.findById(id);
  }

  async delete(id: number): Promise<boolean> {
    const result = await this.casesRepo.delete(id);
    return (result.affected || 0) > 0;
  }

  // ---------------------------------------------------------------------------
  // Learner-owned cases
  // ---------------------------------------------------------------------------

  async updateOwned(id: number, sessionId: string, dto: UpdateCaseDto): Promise<Case | null> {
    await this.assertOwner(id, sessionId);
    return this.update(id, dto);
  }

  async deleteOwned(id: number, sessionId: string): Promise<boolean> {
    await this.assertOwner(id, sessionId);
    return this.delete(id);
  }

  async addEntry(caseId: number, payload: Record<string, any>): Promise<CaseEntry> {
    const caseEntity = await this.casesRepo.findOne({ where: { id: caseId } });
    if (!caseEntity) throw new NotFoundException('Case not found');
    return this.entriesRepo.save(this.entriesRepo.create({ caseId, payload }));
  }

  async deleteEntry(entryId: number): Promise<boolean> {
    const result = await this.entriesRepo.delete(entryId);
    return (result.affected || 0) > 0;
  }

  private async assertOwner(id: number, sessionId: string): Promise<Case> {
    const caseEntity = await this.casesRepo.findOne({ where: { id } });
    if (!caseEntity) throw new NotFoundException('Case not found');
    if (caseEntity.anonymousSessionId !== sessionId) {
      throw new ForbiddenException('This case belongs to a different session');
    }
    return caseEntity;
  }
}
