import { slugify } from "transliteration";
import type { HaloClient } from "./client";
import type { Category, Tag } from "./types";
import { ApiPaths } from "../utils/api-paths";

export class TaxonomyService {
  constructor(private client: HaloClient) {}

  async getCategories(): Promise<Category[]> {
    const data = await this.client.get<{ items: Category[] }>(ApiPaths.categories.list());
    return data.items;
  }

  async getTags(): Promise<Tag[]> {
    const data = await this.client.get<{ items: Tag[] }>(ApiPaths.tags.list());
    return data.items;
  }

  async getCategoryNames(displayNames: string[]): Promise<string[]> {
    const allCategories = await this.getCategories();

    const notExistDisplayNames = displayNames.filter(
      (name) => !allCategories.find((item) => item.spec.displayName === name),
    );

    const newCategories = await Promise.all(
      notExistDisplayNames.map((name, index) =>
        this.createCategory(name, slugify(name, { trim: true }), allCategories.length + index),
      ),
    );

    const existNames = displayNames
      .map((name) => {
        const found = allCategories.find((item) => item.spec.displayName === name);
        return found ? found.metadata.name : undefined;
      })
      .filter(Boolean) as string[];

    return [...existNames, ...newCategories.map((item) => item.metadata.name)];
  }

  async getCategoryDisplayNames(names?: string[]): Promise<string[]> {
    if (!names || names.length === 0) return [];
    const categories = await this.getCategories();
    return names
      .map((name) => {
        const found = categories.find((item) => item.metadata.name === name);
        return found ? found.spec.displayName : undefined;
      })
      .filter(Boolean) as string[];
  }

  async createCategory(displayName: string, slug: string, priority: number): Promise<Category> {
    return await this.client.post<Category>(ApiPaths.categories.create(), {
      spec: {
        displayName,
        slug,
        description: "",
        cover: "",
        template: "",
        priority,
        children: [],
      },
      apiVersion: "content.halo.run/v1alpha1",
      kind: "Category",
      metadata: { name: "", generateName: "category-" },
    });
  }

  async updateCategory(name: string, displayName: string, slug: string, priority: number): Promise<void> {
    const category = await this.client.get<Category>(ApiPaths.categories.get(name));
    category.spec.displayName = displayName;
    category.spec.slug = slug;
    category.spec.priority = priority;
    await this.client.put(ApiPaths.categories.update(name), category);
  }

  async deleteCategory(name: string): Promise<void> {
    await this.client.delete(ApiPaths.categories.delete(name));
  }

  async getTagNames(displayNames: string[]): Promise<string[]> {
    const allTags = await this.getTags();

    const notExistDisplayNames = displayNames.filter(
      (name) => !allTags.find((item) => item.spec.displayName === name),
    );

    const newTags = await Promise.all(
      notExistDisplayNames.map((name) => this.createTag(name, slugify(name, { trim: true }), "#ffffff")),
    );

    const existNames = displayNames
      .map((name) => {
        const found = allTags.find((item) => item.spec.displayName === name);
        return found ? found.metadata.name : undefined;
      })
      .filter(Boolean) as string[];

    return [...existNames, ...newTags.map((item) => item.metadata.name)];
  }

  async getTagDisplayNames(names?: string[]): Promise<string[]> {
    if (!names || names.length === 0) return [];
    const tags = await this.getTags();
    return names
      .map((name) => {
        const found = tags.find((item) => item.metadata.name === name);
        return found ? found.spec.displayName : undefined;
      })
      .filter(Boolean) as string[];
  }

  async createTag(displayName: string, slug: string, color: string): Promise<Tag> {
    return await this.client.post<Tag>(ApiPaths.tags.create(), {
      spec: {
        displayName,
        slug,
        color: color || "#ffffff",
        cover: "",
      },
      apiVersion: "content.halo.run/v1alpha1",
      kind: "Tag",
      metadata: { name: "", generateName: "tag-" },
    });
  }

  async updateTag(name: string, displayName: string, slug: string, color: string): Promise<void> {
    const tag = await this.client.get<Tag>(ApiPaths.tags.get(name));
    tag.spec.displayName = displayName;
    tag.spec.slug = slug;
    tag.spec.color = color || "#ffffff";
    await this.client.put(ApiPaths.tags.update(name), tag);
  }

  async deleteTag(name: string): Promise<void> {
    await this.client.delete(ApiPaths.tags.delete(name));
  }
}
