import { Hype } from '@because/hype'
import { define, stylesToCSS } from '@because/forge'
import { baseStyles, initScript, theme } from '@because/toes/tools'
import { readdir, readlink, stat } from 'fs/promises'
import { join } from 'path'
import type { Child } from 'hono/jsx'

const APPS_DIR = process.env.APPS_DIR!
const TOES_URL = process.env.TOES_URL!

const app = new Hype({ prettyHTML: false })

const Container = define('Container', {
  fontFamily: theme('fonts-sans'),
  padding: '20px',
  paddingTop: 0,
  maxWidth: '800px',
  margin: '0 auto',
  color: theme('colors-text'),
})

const Header = define('Header', {
  marginBottom: '20px',
  paddingBottom: '10px',
  borderBottom: `2px solid ${theme('colors-border')}`,
})

const Title = define('Title', {
  margin: 0,
  fontSize: '24px',
  fontWeight: 'bold',
})

const Subtitle = define('Subtitle', {
  color: theme('colors-textMuted'),
  fontSize: '18px',
  marginTop: '5px',
})

const VersionList = define('VersionList', {
  listStyle: 'none',
  padding: 0,
  margin: '20px 0',
  border: `1px solid ${theme('colors-border')}`,
  borderRadius: theme('radius-md'),
  overflow: 'hidden',
})

const VersionItem = define('VersionItem', {
  padding: '12px 15px',
  borderBottom: `1px solid ${theme('colors-border')}`,
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'space-between',
  states: {
    ':last-child': {
      borderBottom: 'none',
    },
    ':hover': {
      backgroundColor: theme('colors-bgHover'),
    },
  },
})

const VersionLink = define('VersionLink', {
  base: 'a',
  textDecoration: 'none',
  color: theme('colors-link'),
  fontFamily: theme('fonts-mono'),
  fontSize: '15px',
  cursor: 'pointer',
  states: {
    ':hover': {
      textDecoration: 'underline',
    },
  },
})

const Badge = define('Badge', {
  fontSize: '12px',
  padding: '2px 8px',
  borderRadius: theme('radius-md'),
  backgroundColor: theme('colors-bgElement'),
  color: theme('colors-statusRunning'),
  fontWeight: 'bold',
})

const ErrorBox = define('ErrorBox', {
  color: theme('colors-error'),
  padding: '20px',
  backgroundColor: theme('colors-bgElement'),
  borderRadius: theme('radius-md'),
  margin: '20px 0',
})

interface LayoutProps {
  title: string
  subtitle?: string
  children: Child
}

function Layout({ title, subtitle, children }: LayoutProps) {
  return (
    <html>
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>{title}</title>
        <link rel="stylesheet" href="/styles.css" />
      </head>
      <body>
        <script dangerouslySetInnerHTML={{ __html: initScript }} />
        <Container>
          <Header>
            <Title>Versions</Title>
          </Header>
          {children}
        </Container>
      </body>
    </html>
  )
}

app.get('/styles.css', c => c.text(baseStyles + stylesToCSS(), 200, {
  'Content-Type': 'text/css; charset=utf-8',
}))

async function getVersions(appPath: string): Promise<{ name: string; isCurrent: boolean }[]> {
  const entries = await readdir(appPath, { withFileTypes: true })

  let currentTarget = ''
  try {
    currentTarget = await readlink(join(appPath, 'current'))
  } catch { }

  return entries
    .filter(e => e.isDirectory() && /^\d{8}-\d{6}$/.test(e.name))
    .map(e => ({ name: e.name, isCurrent: e.name === currentTarget }))
    .sort((a, b) => b.name.localeCompare(a.name))
}

function formatTimestamp(ts: string): string {
  return `${ts.slice(0, 4)}-${ts.slice(4, 6)}-${ts.slice(6, 8)} ${ts.slice(9, 11)}:${ts.slice(11, 13)}:${ts.slice(13, 15)}`
}

app.get('/', async c => {
  const appName = c.req.query('app')

  if (!appName) {
    return c.html(
      <Layout title="Versions">
        <ErrorBox>Please specify an app name with ?app=&lt;name&gt;</ErrorBox>
      </Layout>
    )
  }

  const appPath = join(APPS_DIR, appName)

  try {
    await stat(appPath)
  } catch {
    return c.html(
      <Layout title="Versions">
        <ErrorBox>App "{appName}" not found</ErrorBox>
      </Layout>
    )
  }

  const versions = await getVersions(appPath)

  if (versions.length === 0) {
    return c.html(
      <Layout title="Versions" subtitle={appName}>
        <ErrorBox>No versions found</ErrorBox>
      </Layout>
    )
  }

  return c.html(
    <Layout title="Versions" subtitle={appName}>
      <VersionList>
        {versions.map(v => (
          <VersionItem>
            <VersionLink
              href={`${TOES_URL}/tool/code?app=${appName}&version=${v.name}`}
            >
              {formatTimestamp(v.name)}
            </VersionLink>
            {v.isCurrent && <Badge>current</Badge>}
          </VersionItem>
        ))}
      </VersionList>
    </Layout>
  )
})

export default app.defaults
