import type { StorageService } from "@/core/storage/types";
import type { Tag, TagCategory, TagScope } from "./types";
import { v4 as uuid } from "uuid";

const NAMESPACE = "tags";

export class TagService {
  constructor(private storage: StorageService) {}

  async getAllTags(): Promise<Tag[]> {
    const all = await this.storage.export(NAMESPACE);
    const tags: Tag[] = [];
    for (const value of Object.values(all)) {
      if (value) tags.push(value as Tag);
    }
    return tags;
  }

  async getTagsByCategory(category: TagCategory): Promise<Tag[]> {
    return this.storage.query<Tag>(
      NAMESPACE,
      (tag) => tag.category === category,
    );
  }

  async getTagsByScope(scope: TagScope, scopeId?: string): Promise<Tag[]> {
    return this.storage.query<Tag>(NAMESPACE, (tag) => {
      if (tag.scope !== scope) return false;
      if (scope === "module" && scopeId) return tag.moduleId === scopeId;
      if (scope === "company" && scopeId) return tag.companyId === scopeId;
      return true;
    });
  }

  async getChildren(parentId: string): Promise<Tag[]> {
    return this.storage.query<Tag>(
      NAMESPACE,
      (tag) => tag.parentId === parentId,
    );
  }

  async createTag(data: Omit<Tag, "id" | "createdAt">): Promise<Tag> {
    const tag: Tag = {
      ...data,
      id: uuid(),
      createdAt: new Date().toISOString(),
    };
    await this.storage.set(NAMESPACE, tag.id, tag);
    return tag;
  }

  async updateTag(
    id: string,
    updates: Partial<Omit<Tag, "id" | "createdAt">>,
  ): Promise<Tag | null> {
    const existing = await this.storage.get<Tag>(NAMESPACE, id);
    if (!existing) return null;
    const updated: Tag = {
      ...existing,
      ...updates,
      updatedAt: new Date().toISOString(),
    };
    await this.storage.set(NAMESPACE, id, updated);
    return updated;
  }

  async deleteTag(id: string): Promise<void> {
    // Also delete children
    const children = await this.getChildren(id);
    for (const child of children) {
      await this.storage.delete(NAMESPACE, child.id);
    }
    await this.storage.delete(NAMESPACE, id);
  }

  async searchTags(query: string): Promise<Tag[]> {
    const lower = query.toLowerCase();
    return this.storage.query<Tag>(
      NAMESPACE,
      (tag) =>
        tag.label.toLowerCase().includes(lower) ||
        (tag.projectCode?.toLowerCase().includes(lower) ?? false),
    );
  }

  /** Bulk import tags (for seed data). Skips tags whose label already exists in same category. */
  async importTags(tags: Omit<Tag, "id" | "createdAt">[]): Promise<number> {
    const existing = await this.getAllTags();
    const existingKeys = new Set(
      existing.map((t) => `${t.category}::${t.label}`),
    );
    let imported = 0;
    for (const data of tags) {
      const key = `${data.category}::${data.label}`;
      if (!existingKeys.has(key)) {
        await this.createTag(data);
        existingKeys.add(key);
        imported++;
      }
    }
    return imported;
  }
}
