import { Hono } from "hono"
import { serveStatic } from "hono/bun"
import { render } from "preact-render-to-string"
import { marked } from "marked"
import { readdirSync } from "fs"
import { mkdir, readdir } from 'node:fs/promises'
import { basename, join } from 'path'
import { Layout } from "./components/Layout"
import { Project } from "./components/Project"

const PROJECTS_DIR = ".."
const WORKSHOP_REPO = "https://github.com/probablycorey/the-rabbit-hole"
const CUBBY_DIR = "./cubby"

const app = new Hono()
app.use('/css/*', serveStatic({ root: './public' }))
app.use('/js/*', serveStatic({ root: './public' }))

async function projects(): Promise<string[]> {
    const subdirs = readdirSync(PROJECTS_DIR, { withFileTypes: true })
        .filter((entry: any) => entry.isDirectory())
        .map((entry: any) => entry.name).sort()

    return subdirs
}

function tsx(node: any) {
    return "<!DOCTYPE html>" + render(<Layout>{node}</Layout>)
}

app.get("/", async (c) => {
    const names = await projects()
    return c.html(tsx(
        <div>
            <h1>Projects</h1>
            <ul>
                {names.map(name => <li key={name}><a href={`/${name}`}>{name}</a></li>)}
            </ul>
        </div>
    ))
})

app.get("/:name", async (c) => {
    const name = c.req.param("name").replace("..", "")
    const readme = await Bun.file(`${PROJECTS_DIR}/${name}/README.md`).text()
    const cubbyPath = join(CUBBY_DIR, `project_${name}`)
    let files: string[] = []

    try {
        files = await readdir(cubbyPath)
    } catch (err: any) {
        if (err.code !== "ENOENT") {
            console.error("Error reading cubby:", err)
        }
    }

    return c.html(tsx(<Project name={name} readme={readme} files={files} />))
})

app.get('/:id/:filename', async c => {
    const { id, filename } = c.req.param()
    const path = join(CUBBY_DIR, `project_${id}`, filename)
    const file = Bun.file(path)
    if (!(await file.exists())) return c.json({ error: 'Not found' }, 404)

    return new Response(file, {
        headers: {
            'Content-Type': file.type || 'application/octet-stream',
        },
    })
})

app.post('/:id/upload', async c => {
    const id = c.req.param('id')

    if (id !== basename(id))
        return c.json({ error: 'Invalid project id' }, 400)

    const folder = join(CUBBY_DIR, `project_${id}`)
    try {
        await mkdir(folder, { recursive: true })

        const form = await c.req.formData()
        const file = form.get('file')
        if (!(file instanceof File))
            return c.json({ error: 'Invalid file' }, 400)

        const filename = basename(file.name)
        const filepath = join(folder, filename)
        await Bun.write(filepath, await file.arrayBuffer())

        return c.redirect(`/${id}`)
    } catch (err) {
        console.error('Upload error:', err)
        return c.json({ error: 'Upload failed' }, 500)
    }
})

app.delete('/:id/delete/:filename', async c => {
    const { id, filename } = c.req.param()

    if (id !== basename(id) || filename !== basename(filename))
        return c.json({ error: 'Invalid project id or filename' }, 400)

    const filepath = join(CUBBY_DIR, `project_${id}`, filename)
    try {
        await Bun.file(filepath).delete()
        return c.json({ success: true })
    } catch (err) {
        console.error('Delete error:', err)
        return c.json({ error: 'Delete failed' }, 500)
    }
})

export default {
    port: process.env.PORT || 3000,
    fetch: app.fetch,
    maxRequestBodySize: 1024 * 1024 * 1024, // 1GB
}