/**
 * Load all posts from my blog database and write them to Markdown file
 *
 * usage:
 *
 * node db_to_md.js db_username db_password
 */

import { dirname, join } from 'node:path'
import { fileURLToPath } from 'node:url'
import * as fs from 'node:fs'
import mysql from 'mysql2/promise'

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

class BlogToMarkdown {
    databaseName = 'godruoyi'

    host = '127.0.0.1'

    port = 13306

    constructor() {
        const { username, password } = this.parseProcessArgs()

        this.username = username
        this.password = password
    }

    async run() {
        this.connection = await this.connectMySQL(this.username, this.password)

        const categoryMap = await this.fetchCategories()
        const posts = await this.fetchPosts()

        const formatedPosts = this.formatPosts(categoryMap, posts)

        for (const p of formatedPosts) {
            // this.writePostToMarkdown(p)
            await this.savePostBanner(p)
        }

        console.log('All done')
    }

    async savePostBanner(post) {
        const filePath = this.postBannerFilePath(post)
        console.log('savePostBanner', filePath)

        const response = await fetch(post.banner)

        if (!response.ok) {
            throw new Error('download image failed')
        }

        const arrayBuffer = await response.arrayBuffer()
        fs.writeFileSync(filePath, Buffer.from(arrayBuffer))
    }

    async fetchPosts() {
        const query = 'SELECT * FROM posts ORDER BY id ASC'

        const [rows] = await this.connection.execute(query)

        return rows
    }

    async fetchCategories() {
        const query = 'SELECT * FROM categories ORDER BY id ASC'

        const [rows] = await this.connection.execute(query)

        return rows.reduce((map, obj) => map.set(obj.id, obj), new Map())
    }

    writePostToMarkdown(post) {
        console.log(`saving post to markdown: ${post.title}`)

        const file = this.postToMarkdownFilePath(post)
        const content = this.convertPostToMarkdown(post)

        fs.writeFileSync(file, content, 'utf-8')
    }

    postToMarkdownFilePath(p) {
        return join(__dirname, `./../src/content/posts/${p.slug}.md`)
    }

    postBannerFilePath(p) {
        const name = new URL(p.banner).pathname.split('/').pop()
        return join(__dirname, `./../src/images/posts/${name}`)
    }

    convertPostToMarkdown(post) {
        return `---
title: "${post.title}"
description: "${post.description}"
pubDate: "${this.formatDate(post.pubDate)}"
category: "${post.category.slug}"
cardImage: "${post.banner}"
tags: ["${post.category.slug}"]
oldViewCount: ${post.viewCount}
oldKeywords: ["${post.keyword}"]
---

${post.content}
`
    }

    formatPosts(categoryMap, posts) {
        return posts.map((p) => {
            return {
                title: p.title,
                description: p.excerpt,
                pubDate: p.created_at,
                banner: p.banner,
                category_id: p.category_id,
                category: categoryMap.get(p.category_id),
                slug: p.slug,
                viewCount: p.view_count,
                content: p.content,
                id: p.id,
                keyword: p.keyword,
            }
        })
    }

    async connectMySQL(username, password) {
        return mysql.createConnection({
            host: this.host,
            port: this.port,
            user: username,
            password,
            database: this.databaseName,
        })
    }

    closeConnection() {
        if (this.connection) {
            this.connection.close()
        }
    }

    parseProcessArgs() {
        const username = process.argv[2]
        const password = process.argv[3]

        if (!username || !password) {
            throw new Error('Missing username or password')
        }

        return { username, password }
    }

    formatDate(date) {
        const newDate = new Date(date.getTime() + 8 * 60 * 60 * 1000) // Date object uses milliseconds

        const yyyy = newDate.getUTCFullYear()
        const mm = String(newDate.getUTCMonth() + 1).padStart(2, '0') // Months are zero based
        const dd = String(newDate.getUTCDate()).padStart(2, '0')

        const hh = String(newDate.getUTCHours()).padStart(2, '0')
        const mi = String(newDate.getUTCMinutes()).padStart(2, '0')
        const ss = String(newDate.getUTCSeconds()).padStart(2, '0')

        return `${yyyy}-${mm}-${dd} ${hh}:${mi}:${ss}`
    }
}

const x = new BlogToMarkdown()
await x.run()
await process.exit(0)
