import { db } from "./db";
import { words, examples, tags, wordTags, seeAlso } from "./db/schema";
import { eq } from "drizzle-orm";
import type { Word, Tag, NewWord } from "@/types/types";

class PGStorage {
  async getAllWords(): Promise<Word[]> {
    const allWords = await db.query.words.findMany({
      with: {
        examples: true,
        tags: {
          with: {
            tag: true,
          },
        },
        seeAlso: true,
      },
    });

    return allWords.map((word) => ({
      id: word.id,
      hindi: word.hindi,
      english: word.english,
      type: word.type,
      gender: word.gender as "m" | "f" | null,
      note: word.note,
      examples: word.examples.length > 0 ? word.examples : undefined,
      tags: word.tags.map((wt) => wt.tag),
      seeAlso: word.seeAlso.length > 0 ? word.seeAlso : undefined,
    }));
  }

  async getWordById(id: number): Promise<Word | null> {
    const word = await db.query.words.findFirst({
      where: eq(words.id, id),
      with: {
        examples: true,
        tags: {
          with: {
            tag: true,
          },
        },
        seeAlso: true,
      },
    });

    if (!word) return null;

    return {
      id: word.id,
      hindi: word.hindi,
      english: word.english,
      type: word.type,
      gender: word.gender as "m" | "f" | null,
      note: word.note,
      examples: word.examples.length > 0 ? word.examples : undefined,
      tags: word.tags.map((wt) => wt.tag),
      seeAlso: word.seeAlso.length > 0 ? word.seeAlso : undefined,
    };
  }

  async getWordsByTag(tagName: string): Promise<Word[]> {
    const tag = await db.query.tags.findFirst({
      where: eq(tags.name, tagName),
      with: {
        words: {
          with: {
            word: {
              with: {
                examples: true,
                tags: {
                  with: {
                    tag: true,
                  },
                },
                seeAlso: true,
              },
            },
          },
        },
      },
    });

    if (!tag) return [];

    return tag.words.map((wt) => ({
      id: wt.word.id,
      hindi: wt.word.hindi,
      english: wt.word.english,
      type: wt.word.type,
      gender: wt.word.gender as "m" | "f" | null,
      note: wt.word.note,
      examples: wt.word.examples.length > 0 ? wt.word.examples : undefined,
      tags: wt.word.tags.map((t) => t.tag),
      seeAlso: wt.word.seeAlso.length > 0 ? wt.word.seeAlso.map(sa => ({
        ...sa,
        reference: sa.reference || '',
      })) : undefined,
    }));
  }

  async getAllTags(): Promise<Tag[]> {
    return await db.query.tags.findMany();
  }

  async getAllTypes(): Promise<string[]> {
    const types = new Set<string>();
    const allWords = await this.getAllWords();
    allWords.forEach((word) => {
      if (word.type) types.add(word.type);
    });
    return Array.from(types).sort();
  }

  async createWord(newWord: NewWord): Promise<Word> {
    const [word] = await db
      .insert(words)
      .values({
        hindi: newWord.hindi,
        english: newWord.english,
        type: newWord.type,
        gender: newWord.gender || null,
        note: newWord.note || null,
      })
      .returning();

    // Add examples
    if (newWord.examples) {
      for (const example of newWord.examples) {
        await db.insert(examples).values({
          wordId: word.id,
          hindi: example.hindi,
          english: example.english,
          note: example.note || null,
        });
      }
    }

    // Add tags
    if (newWord.tagIds) {
      for (const tagId of newWord.tagIds) {
        await db.insert(wordTags).values({
          wordId: word.id,
          tagId: tagId,
        });
      }
    }

    // Add see also references
    if (newWord.seeAlso) {
      for (const ref of newWord.seeAlso) {
        await db.insert(seeAlso).values({
          wordId: word.id,
          reference: ref.reference,
          note: ref.note || null,
        });
      }
    }

    // Fetch the complete word with all relations
    const createdWord = await this.getWordById(word.id);
    return createdWord!;
  }

  async createTag(name: string, description?: string): Promise<Tag> {
    const [tag] = await db
      .insert(tags)
      .values({
        name,
        description: description || null,
      })
      .returning();

    return tag;
  }

  async deleteWord(id: number): Promise<void> {
    await db.delete(words).where(eq(words.id, id));
  }
}

export const storage = new PGStorage();
