/**
 * @copyright 2026 NHCarrigan
 * @license Naomi's Public License
 * @author Naomi Carrigan
 */

import { Art, CreateArtDto, UpdateArtDto } from "@library/shared-types";
import { prisma } from "../lib/prisma";
import {
  validateUrl,
  validateStringLength,
  MAX_LENGTHS,
} from "../utils/validation";

export class ArtService {
  private prisma = prisma;

  constructor() {}

  /**
   * Validate art data for security.
   */
  private validateArtData(data: CreateArtDto | UpdateArtDto): void {
    // Validate string lengths
    if (!validateStringLength(data.title, MAX_LENGTHS.TITLE)) {
      throw new Error(`Title must be ${MAX_LENGTHS.TITLE} characters or less.`);
    }
    if (!validateStringLength(data.artist, MAX_LENGTHS.AUTHOR)) {
      throw new Error(`Artist must be ${MAX_LENGTHS.AUTHOR} characters or less.`);
    }
    if (!validateStringLength(data.description, MAX_LENGTHS.DESCRIPTION)) {
      throw new Error(`Description must be ${MAX_LENGTHS.DESCRIPTION} characters or less.`);
    }

    // Validate image URL (required)
    if (!data.imageUrl) {
      throw new Error("Image URL is required.");
    }
    if (!validateUrl(data.imageUrl)) {
      throw new Error("Invalid image URL. Only http and https URLs are allowed.");
    }
    if (!validateStringLength(data.imageUrl, MAX_LENGTHS.URL)) {
      throw new Error(`Image URL must be ${MAX_LENGTHS.URL} characters or less.`);
    }

    // Validate tags
    if (data.tags) {
      for (const tag of data.tags) {
        if (!validateStringLength(tag, MAX_LENGTHS.TAGS)) {
          throw new Error(`Each tag must be ${MAX_LENGTHS.TAGS} characters or less.`);
        }
      }
    }

    // Validate link URLs
    if (data.links) {
      for (const link of data.links) {
        if (!validateUrl(link.url)) {
          throw new Error(`Invalid link URL: ${link.title}. Only http and https URLs are allowed.`);
        }
        if (!validateStringLength(link.title, MAX_LENGTHS.AUTHOR)) {
          throw new Error(`Link title must be ${MAX_LENGTHS.AUTHOR} characters or less.`);
        }
        if (!validateStringLength(link.url, MAX_LENGTHS.URL)) {
          throw new Error(`Link URL must be ${MAX_LENGTHS.URL} characters or less.`);
        }
      }
    }
  }

  /**
   * Get all art pieces.
   */
  async getAllArt(): Promise<Art[]> {
    const artPieces = await this.prisma.art.findMany({
      orderBy: { createdAt: "desc" },
    });

    return artPieces.map((art) => ({
      ...art,
      description: art.description || undefined,
      tags: art.tags ?? [],
      links: art.links ?? [],
      dateAdded: art.dateAdded,
      createdAt: art.createdAt,
      updatedAt: art.updatedAt,
    }));
  }

  /**
   * Get art by ID.
   */
  async getArtById(id: string): Promise<Art | null> {
    const art = await this.prisma.art.findUnique({
      where: { id },
    });

    if (!art) return null;

    return {
      ...art,
      description: art.description || undefined,
      tags: art.tags ?? [],
      links: art.links ?? [],
      dateAdded: art.dateAdded,
      createdAt: art.createdAt,
      updatedAt: art.updatedAt,
    };
  }

  /**
   * Create new art piece.
   */
  async createArt(data: CreateArtDto): Promise<Art> {
    // Validate input
    this.validateArtData(data);

    const art = await this.prisma.art.create({
      data,
    });

    return {
      ...art,
      description: art.description || undefined,
      tags: art.tags ?? [],
      links: art.links ?? [],
      dateAdded: art.dateAdded,
      createdAt: art.createdAt,
      updatedAt: art.updatedAt,
    };
  }

  /**
   * Update art by ID.
   */
  async updateArt(id: string, data: UpdateArtDto): Promise<Art> {
    // Validate input
    this.validateArtData(data);

    const art = await this.prisma.art.update({
      where: { id },
      data,
    });

    return {
      ...art,
      description: art.description || undefined,
      tags: art.tags ?? [],
      links: art.links ?? [],
      dateAdded: art.dateAdded,
      createdAt: art.createdAt,
      updatedAt: art.updatedAt,
    };
  }

  /**
   * Delete art by ID.
   */
  async deleteArt(id: string): Promise<void> {
    await this.prisma.art.delete({
      where: { id },
    });
  }
}
