import { Injectable, HttpStatus, NotFoundException, BadRequestException } from '@nestjs/common';
import { Kysely, sql } from 'kysely';
import { DB } from '@/database/database.type';
import { SubmitStoryDto } from './stories.dto';
import { containsDisallowedUserContent, MODERATION_REJECTION_MESSAGE } from '@/utils/content-moderation';

@Injectable()
export class StoriesService {
  constructor(private readonly db: Kysely<DB>) {}

  async getPublicStories() {
    const result = await this.db
      .selectFrom('reunited_stories')
      .selectAll()
      .where('is_published', '=', true)
      .where('deleted_at', 'is', null)
      .orderBy('reunion_date', 'desc')
      .execute();

    return { status: true, statusCode: HttpStatus.OK, data: result };
  }

  async getStoryBySlug(slug: string) {
    const result = await this.db
      .selectFrom('reunited_stories')
      .selectAll()
      .where('slug', '=', slug)
      .where('deleted_at', 'is', null)
      .executeTakeFirst();

    if (!result) {
      throw new NotFoundException('Story not found');
    }
    return { status: true, statusCode: HttpStatus.OK, data: result };
  }

  async submitStory(user_id: string, data: SubmitStoryDto) {
    const { claimId, report_id, title, content, howTheyReunited } = data;

    if (containsDisallowedUserContent([title, content, howTheyReunited])) {
      throw new BadRequestException(MODERATION_REJECTION_MESSAGE);
    }

    let lostReportId: string | null = null;
    let foundReportId: string | null = null;
    let mainReport: any = null;
    let otherReport: any = null;

    if (claimId) {
      const claim = await this.db
        .selectFrom('claim_requests')
        .selectAll()
        .where('id', '=', claimId)
        .where('deleted_at', 'is', null)
        .executeTakeFirst();

      if (!claim) {
        throw new NotFoundException('Claim not found');
      }
      lostReportId = claim.lost_report_id;
      foundReportId = claim.found_report_id;

      mainReport = await this.db
        .selectFrom('pet_reports')
        .selectAll()
        .where('id', '=', lostReportId)
        .where('deleted_at', 'is', null)
        .executeTakeFirst();
      otherReport = await this.db
        .selectFrom('pet_reports')
        .selectAll()
        .where('id', '=', foundReportId)
        .where('deleted_at', 'is', null)
        .executeTakeFirst();
    } else if (report_id) {
      mainReport = await this.db
        .selectFrom('pet_reports')
        .selectAll()
        .where('id', '=', report_id)
        .where('deleted_at', 'is', null)
        .executeTakeFirst();

      if (!mainReport) {
        throw new NotFoundException('Report not found');
      }

      const sharedId = mainReport.pet_reunited_id;
      if (sharedId) {
        otherReport = await this.db
          .selectFrom('pet_reports')
          .selectAll()
          .where('pet_reunited_id', '=', sharedId)
          .where('id', '!=', report_id)
          .where('deleted_at', 'is', null)
          .executeTakeFirst();
      }
    }

    if (!mainReport) {
      throw new BadRequestException('Could not resolve main report');
    }

    const slug = `${String(mainReport.pet_name).toLowerCase().replace(/[^a-z0-9]+/g, '-')}-${Date.now()}`;

    const user = await this.db
      .selectFrom('users')
      .select('display_name')
      .where('id', '=', user_id)
      .where('deleted_at', 'is', null)
      .executeTakeFirst();
    const ownerName = user?.display_name || 'Owner';

    const inserted = await this.db
      .insertInto('reunited_stories')
      .values({
        slug,
        pet_name: mainReport.pet_name,
        pet_type: mainReport.pet_type,
        owner_name: ownerName,
        story_title: title,
        story_content: content,
        before_image_url: mainReport.photo_uri,
        after_image_url: otherReport?.photo_uri || null,
        reunion_date: sql`NOW()`,
        how_they_reunited: howTheyReunited || null,
        is_published: true,
      })
      .returning('id')
      .executeTakeFirstOrThrow();

    const storyId = inserted.id;

    // Flip the linked reports to 'reunited'
    if (claimId && lostReportId && foundReportId) {
      await this.db
        .updateTable('pet_reports')
        .set({
          status: 'reunited',
          reunion_date: sql`NOW()`,
          pet_reunited_id: storyId,
          is_reunited: true,
        })
        .where('id', 'in', [lostReportId, foundReportId])
        .where('deleted_at', 'is', null)
        .execute();
    } else {
      const sharedId = mainReport.pet_reunited_id;
      const update = this.db.updateTable('pet_reports').set({
        pet_reunited_id: storyId,
        status: 'reunited',
        reunion_date: sql`NOW()`,
        is_reunited: true,
      });
      if (sharedId) {
        await update.where('pet_reunited_id', '=', sharedId).where('deleted_at', 'is', null).execute();
      } else {
        await update.where('id', '=', mainReport.id).where('deleted_at', 'is', null).execute();
      }
    }

    return { status: true, statusCode: HttpStatus.CREATED, data: { storyId } };
  }
}
