import { request, type Page } from './client';

/** Row returned by `GET /v1/me/page-tags`. */
export type TaggedPageItem = {
    tag: string;
    page_id: string;
    chapter_id: string;
    manga_id: string;
    page_number: number;
    chapter_number: number;
    chapter_title: string | null;
    manga_title: string;
    storage_key: string;
    tagged_at: string;
};

export type MyPageTagsPage = {
    items: TaggedPageItem[];
    page: Page;
};

export type PageTagSummary = {
    tag: string;
    count: number;
};

export type ListMineOptions = {
    /** Exact-match filter (chip filter in the library tab). */
    tag?: string;
    /** Prefix filter (autocomplete-style). */
    q?: string;
    limit?: number;
    offset?: number;
};

export async function getMyTagsForPage(pageId: string): Promise<string[]> {
    const r = await request<{ tags: string[] }>(
        `/v1/pages/${encodeURIComponent(pageId)}/my-tags`
    );
    return r.tags;
}

export async function addTagToPage(pageId: string, tag: string): Promise<void> {
    await request<void>(`/v1/pages/${encodeURIComponent(pageId)}/tags`, {
        method: 'POST',
        headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ tag })
    });
}

export async function removeTagFromPage(pageId: string, tag: string): Promise<void> {
    await request<void>(
        `/v1/pages/${encodeURIComponent(pageId)}/tags/${encodeURIComponent(tag)}`,
        { method: 'DELETE' }
    );
}

export async function listMyPageTags(
    opts: ListMineOptions = {}
): Promise<MyPageTagsPage> {
    const params = new URLSearchParams();
    if (opts.tag != null) params.set('tag', opts.tag);
    if (opts.q != null) params.set('q', opts.q);
    if (opts.limit != null) params.set('limit', String(opts.limit));
    if (opts.offset != null) params.set('offset', String(opts.offset));
    const qs = params.toString();
    return request<MyPageTagsPage>(`/v1/me/page-tags${qs ? `?${qs}` : ''}`);
}

/** Closed content-warning vocabulary the analysis worker can flag. */
export type ContentWarning = 'sexual' | 'nudity' | 'gore' | 'violence' | 'disturbing';

export const CONTENT_WARNINGS: ContentWarning[] = [
    'sexual',
    'nudity',
    'gore',
    'violence',
    'disturbing'
];

/** Row returned by `GET /v1/me/page-search` (one per matching page). */
export type PageSearchItem = {
    page_id: string;
    chapter_id: string;
    manga_id: string;
    page_number: number;
    chapter_number: number;
    chapter_title: string | null;
    manga_title: string;
    storage_key: string;
    is_nsfw: boolean;
    content_warnings: ContentWarning[];
    rank: number;
};

export type PageSearchPage = {
    items: PageSearchItem[];
    page: Page;
};

export type PageSearchOptions = {
    /** AND-ed tags (own page tags ∪ global auto-tags). */
    tags?: string[];
    /** Free-text query over OCR + scene description. */
    text?: string;
    /** Content warnings the page must carry (all). */
    cwInclude?: ContentWarning[];
    /** Content warnings the page must not carry (any). */
    cwExclude?: ContentWarning[];
    limit?: number;
    offset?: number;
};

/**
 * Content search over the caller's pages. The backend requires at least
 * one positive filter (tags, text, or cwInclude) — calling with none is a
 * 422.
 */
export async function searchPages(opts: PageSearchOptions = {}): Promise<PageSearchPage> {
    const params = new URLSearchParams();
    if (opts.tags && opts.tags.length) params.set('tags', opts.tags.join(','));
    if (opts.text != null && opts.text !== '') params.set('text', opts.text);
    if (opts.cwInclude && opts.cwInclude.length)
        params.set('cw_include', opts.cwInclude.join(','));
    if (opts.cwExclude && opts.cwExclude.length)
        params.set('cw_exclude', opts.cwExclude.join(','));
    if (opts.limit != null) params.set('limit', String(opts.limit));
    if (opts.offset != null) params.set('offset', String(opts.offset));
    return request<PageSearchPage>(`/v1/me/page-search?${params.toString()}`);
}

export async function listMyDistinctPageTags(
    prefix?: string,
    limit?: number
): Promise<PageTagSummary[]> {
    const params = new URLSearchParams();
    if (prefix != null && prefix !== '') params.set('q', prefix);
    if (limit != null) params.set('limit', String(limit));
    const qs = params.toString();
    const r = await request<{ items: PageTagSummary[] }>(
        `/v1/me/page-tags/distinct${qs ? `?${qs}` : ''}`
    );
    return r.items;
}

/** Row returned by `GET /v1/me/page-tags/chapters`. */
export type TaggedChapterAggregate = {
    chapter_id: string;
    manga_id: string;
    manga_title: string;
    chapter_number: number;
    chapter_title: string | null;
    match_count: number;
    /** Up to 3 storage keys of matching pages, page-number ascending. */
    sample_storage_keys: string[];
};

/** Row returned by `GET /v1/me/page-tags/mangas`. */
export type TaggedMangaAggregate = {
    manga_id: string;
    manga_title: string;
    manga_cover_image_path: string | null;
    match_count: number;
    sample_storage_keys: string[];
};

export type TaggedChaptersPage = {
    items: TaggedChapterAggregate[];
    page: Page;
};

export type TaggedMangasPage = {
    items: TaggedMangaAggregate[];
    page: Page;
};

export type AggregateOptions = {
    tag: string;
    order?: 'desc' | 'asc';
    limit?: number;
    offset?: number;
};

function aggregateQs(opts: AggregateOptions): string {
    const params = new URLSearchParams();
    params.set('tag', opts.tag);
    if (opts.order != null) params.set('order', opts.order);
    if (opts.limit != null) params.set('limit', String(opts.limit));
    if (opts.offset != null) params.set('offset', String(opts.offset));
    return params.toString();
}

export async function listTaggedChapters(
    opts: AggregateOptions
): Promise<TaggedChaptersPage> {
    return request<TaggedChaptersPage>(
        `/v1/me/page-tags/chapters?${aggregateQs(opts)}`
    );
}

export async function listTaggedMangas(
    opts: AggregateOptions
): Promise<TaggedMangasPage> {
    return request<TaggedMangasPage>(
        `/v1/me/page-tags/mangas?${aggregateQs(opts)}`
    );
}
