import { type App, Notice, type TFile } from "obsidian";
import { randomUUID } from "../utils/id";
import markdownIt from "../utils/markdown";
import { slugify } from "transliteration";
import type { HaloSetting, HaloSite } from "../settings";
import { HaloClient } from "./client";
import { PostService } from "./post-service";
import { ImageService } from "./image-service";
import { TaxonomyService } from "./taxonomy-service";
import { ContentService } from "./content-service";
import type { Content, Post } from "./types";
import { HaloError } from "./error";
import { logger } from "../utils/logger";

export class HaloService {
  private readonly app: App;
  private readonly siteUrl: string;
  private readonly settings: HaloSetting;
  private readonly client: HaloClient;
  private readonly postService: PostService;
  private readonly imageService: ImageService;
  private readonly taxonomyService: TaxonomyService;
  private readonly contentService: ContentService;

  constructor(app: App, settings: HaloSetting, site: HaloSite) {
    this.app = app;
    this.siteUrl = site.url;
    this.settings = settings;
    this.client = new HaloClient(site);
    this.postService = new PostService(app, this.client);
    this.imageService = new ImageService(site);
    this.taxonomyService = new TaxonomyService(this.client);
    this.contentService = new ContentService();
  }

  async publishPost(): Promise<void> {
    const { activeEditor } = this.app.workspace;

    if (!activeEditor || !activeEditor.file) {
      return;
    }

    let processedRaw = await this.app.vault.read(activeEditor.file);
    const matterData = this.app.metadataCache.getFileCache(activeEditor.file)?.frontmatter;
    const frontmatterPosition = this.app.metadataCache.getFileCache(activeEditor.file)?.frontmatterPosition;

    processedRaw = frontmatterPosition ? processedRaw.slice(frontmatterPosition.end.offset) : processedRaw;

    // Process images if enabled
    if (this.settings.imageUpload?.enabled) {
      const imageResult = await this.imageService.uploadLocalImages(
        processedRaw,
        this.app.vault,
        activeEditor.file.path,
      );

      if (imageResult.successCount > 0) {
        new Notice(`图片上传成功 (${imageResult.successCount}/${imageResult.successCount + imageResult.failCount})`);
      } else if (imageResult.failCount > 0) {
        new Notice(`部分图片上传失败 (失败: ${imageResult.failCount})`);
      }

      processedRaw = imageResult.processedContent;
    }

    // Build content
    const content: Content = {
      rawType: "markdown",
      raw: processedRaw,
      content: markdownIt.render(processedRaw),
    };

    // Get existing post or create new params
    let params: Post;
    let isUpdate = false;

    if (matterData?.halo?.name) {
      const existingPost = await this.postService.getPost(matterData.halo.name);
      if (existingPost) {
        params = existingPost.post;
        content.raw = processedRaw;
        content.content = markdownIt.render(processedRaw);
        isUpdate = true;
      } else {
        params = this.createDefaultPostParams();
      }
    } else {
      params = this.createDefaultPostParams();
    }

    // Update params from frontmatter
    params.spec.title = matterData?.title || activeEditor.file.basename;
    params.spec.slug = matterData?.slug || slugify(params.spec.title, { trim: true });

    if (matterData?.excerpt) {
      params.spec.excerpt.raw = matterData.excerpt;
      params.spec.excerpt.autoGenerate = false;
    }

    if (matterData?.cover) {
      params.spec.cover = matterData.cover;
    }

    if (matterData?.categories) {
      params.spec.categories = await this.taxonomyService.getCategoryNames(matterData.categories);
    }

    if (matterData?.tags) {
      params.spec.tags = await this.taxonomyService.getTagNames(matterData.tags);
    }

    // Set content annotation
    params.metadata.annotations = {
      ...params.metadata.annotations,
      "content.halo.run/content-json": JSON.stringify(content),
    };

    // Ensure metadata.name exists
    if (!params.metadata.name) {
      params.metadata.name = randomUUID();
    }

    try {
      if (isUpdate) {
        await this.postService.updatePost(params.metadata.name, params);
      } else {
        const newPost = await this.postService.createPost(params);
        params.metadata.name = newPost.metadata.name;
      }

      // Handle publish status
      if (matterData?.halo?.hasOwnProperty("publish")) {
        await this.postService.changePublishStatus(params.metadata.name, matterData.halo.publish);
      } else if (this.settings.publishByDefault) {
        await this.postService.changePublishStatus(params.metadata.name, true);
      }

      // Refresh post to get latest data
      const postResult = await this.postService.getPost(params.metadata.name);
      if (!postResult || !postResult.post.metadata) {
        new Notice("发布失败：无法获取文章详情");
        return;
      }

      params = postResult.post;

      // Update frontmatter
      const postCategories = await this.taxonomyService.getCategoryDisplayNames(params.spec.categories);
      const postTags = await this.taxonomyService.getTagDisplayNames(params.spec.tags);

      this.app.fileManager.processFrontMatter(activeEditor.file, (frontmatter) => {
        frontmatter.title = params.spec.title;
        frontmatter.slug = params.spec.slug;
        frontmatter.cover = params.spec.cover;
        frontmatter.excerpt = params.spec.excerpt.autoGenerate ? undefined : params.spec.excerpt.raw;
        frontmatter.categories = postCategories;
        frontmatter.tags = postTags;
        frontmatter.halo = {
          site: this.siteUrl,
          name: params.metadata.name,
          publish: params.spec.publish,
        };
      });

      new Notice("发布成功");
    } catch (error) {
      if (error instanceof HaloError) {
        logger.error("HaloService", `发布失败 [${error.code}]`, error.message);
        new Notice(error.message);
      } else {
        logger.error("HaloService", "发布失败", error);
        new Notice("发布失败");
      }
    }
  }

  async updatePost(): Promise<void> {
    const { activeEditor } = this.app.workspace;

    if (!activeEditor || !activeEditor.file) {
      return;
    }

    const matterData = this.app.metadataCache.getFileCache(activeEditor.file)?.frontmatter;

    if (!matterData?.halo?.name) {
      new Notice("文章尚未发布到 Halo");
      return;
    }

    const post = await this.postService.getPost(matterData.halo.name);

    if (!post) {
      new Notice("在 Halo 中未找到文章");
      return;
    }

    const postCategories = await this.taxonomyService.getCategoryDisplayNames(post.post.spec.categories);
    const postTags = await this.taxonomyService.getTagDisplayNames(post.post.spec.tags);

    await this.app.vault.modify(activeEditor.file, post.content.raw);

    this.app.fileManager.processFrontMatter(activeEditor.file, (frontmatter) => {
      frontmatter.title = post.post.spec.title;
      frontmatter.slug = post.post.spec.slug;
      frontmatter.cover = post.post.spec.cover;
      frontmatter.excerpt = post.post.spec.excerpt.autoGenerate ? undefined : post.post.spec.excerpt.raw;
      frontmatter.categories = postCategories;
      frontmatter.tags = postTags;
      frontmatter.halo = {
        site: this.siteUrl,
        name: post.post.metadata.name,
        publish: post.post.spec.publish,
      };
    });
  }

  async pullPost(name: string): Promise<void> {
    const post = await this.postService.getPost(name);

    if (!post) {
      new Notice("在 Halo 中未找到文章");
      return;
    }

    const postCategories = await this.taxonomyService.getCategoryDisplayNames(post.post.spec.categories);
    const postTags = await this.taxonomyService.getTagDisplayNames(post.post.spec.tags);

    const file = await this.app.vault.create(`${post.post.spec.title}.md`, post.content.raw);
    this.app.workspace.getLeaf().openFile(file);

    this.app.fileManager.processFrontMatter(file, (frontmatter) => {
      frontmatter.title = post.post.spec.title;
      frontmatter.slug = post.post.spec.slug;
      frontmatter.cover = post.post.spec.cover;
      frontmatter.excerpt = post.post.spec.excerpt.autoGenerate ? undefined : post.post.spec.excerpt.raw;
      frontmatter.categories = postCategories;
      frontmatter.tags = postTags;
      frontmatter.halo = {
        site: this.siteUrl,
        name: name,
        publish: post.post.spec.publish,
      };
    });
  }

  async importPost(file: TFile, publishImmediately: boolean = false): Promise<boolean> {
    try {
      let raw = await this.app.vault.read(file);
      const matterData = this.app.metadataCache.getFileCache(file)?.frontmatter;
      const frontmatterPosition = this.app.metadataCache.getFileCache(file)?.frontmatterPosition;

      raw = frontmatterPosition ? raw.slice(frontmatterPosition.end.offset) : raw;

      // Process images if enabled
      if (this.settings.imageUpload?.enabled) {
        const imageResult = await this.imageService.uploadLocalImages(raw, this.app.vault, file.path);

        if (imageResult.successCount > 0) {
          new Notice(`图片上传成功 (${imageResult.successCount}/${imageResult.successCount + imageResult.failCount})`);
        }

        raw = imageResult.processedContent;
      }

      const content: Content = {
        rawType: "markdown",
        raw: raw,
        content: markdownIt.render(raw),
      };

      const params: Post = {
        apiVersion: "content.halo.run/v1alpha1",
        kind: "Post",
        metadata: {
          annotations: {
            "content.halo.run/content-json": JSON.stringify(content),
          },
          name: randomUUID(),
        },
        spec: {
          allowComment: true,
          baseSnapshot: "",
          categories: [],
          cover: matterData?.cover || "",
          deleted: false,
          excerpt: {
            autoGenerate: matterData?.excerpt ? false : true,
            raw: matterData?.excerpt || "",
          },
          headSnapshot: "",
          htmlMetas: [],
          owner: "",
          pinned: false,
          priority: 0,
          publish: publishImmediately,
          publishTime: "",
          releaseSnapshot: "",
          slug: matterData?.slug || slugify(matterData?.title || file.basename, { trim: true }),
          tags: [],
          template: "",
          title: matterData?.title || file.basename,
          visible: "PUBLIC",
        },
      };

      // Handle categories
      if (matterData?.categories) {
        params.spec.categories = await this.taxonomyService.getCategoryNames(matterData.categories);
      }

      // Handle tags
      if (matterData?.tags) {
        params.spec.tags = await this.taxonomyService.getTagNames(matterData.tags);
      }

      // Create post
      const post = await this.postService.createPost(params);

      return !!post && !!post.metadata;
    } catch (error) {
      if (error instanceof HaloError) {
        logger.error("HaloService", `导入文章失败 [${error.code}]`, error.message);
      } else {
        logger.error("HaloService", "导入文章失败", error);
      }
      return false;
    }
  }

  async deletePost(name: string): Promise<boolean> {
    try {
      await this.postService.deletePost(name);
      return true;
    } catch (error) {
      if (error instanceof HaloError) {
        logger.error("HaloService", `删除文章失败 [${error.code}]`, error.message);
      } else {
        logger.error("HaloService", "删除文章失败", error);
      }
      return false;
    }
  }

  async createTag(displayName: string, slug: string, color: string): Promise<void> {
    await this.taxonomyService.createTag(displayName, slug, color);
  }

  async updateTag(name: string, displayName: string, slug: string, color: string): Promise<void> {
    await this.taxonomyService.updateTag(name, displayName, slug, color);
  }

  async deleteTag(name: string): Promise<void> {
    await this.taxonomyService.deleteTag(name);
  }

  async createCategory(displayName: string, slug: string, priority: number): Promise<void> {
    await this.taxonomyService.createCategory(displayName, slug, priority);
  }

  async updateCategory(name: string, displayName: string, slug: string, priority: number): Promise<void> {
    await this.taxonomyService.updateCategory(name, displayName, slug, priority);
  }

  async deleteCategory(name: string): Promise<void> {
    await this.taxonomyService.deleteCategory(name);
  }

  private createDefaultPostParams(): Post {
    return {
      apiVersion: "content.halo.run/v1alpha1",
      kind: "Post",
      metadata: {
        annotations: {},
        name: "",
      },
      spec: {
        allowComment: true,
        baseSnapshot: "",
        categories: [],
        cover: "",
        deleted: false,
        excerpt: {
          autoGenerate: true,
          raw: "",
        },
        headSnapshot: "",
        htmlMetas: [],
        owner: "",
        pinned: false,
        priority: 0,
        publish: false,
        publishTime: "",
        releaseSnapshot: "",
        slug: "",
        tags: [],
        template: "",
        title: "",
        visible: "PUBLIC",
      },
    };
  }
}

export default HaloService;
