export interface FileBrowserItem {
  name: string
  path: string
  size: number
  modified: string
  isDir: boolean
  type: string
  extension: string
}

interface FileBrowserListResponse {
  items: FileBrowserItem[]
  numDirs: number
  numFiles: number
  sorting: { by: string; asc: boolean }
}

class FileBrowserClient {
  private token: string | null = null
  private baseUrl: string

  constructor() {
    this.baseUrl = `${window.location.origin}/app/filebrowser`
  }

  get isAuthenticated(): boolean {
    return this.token !== null
  }

  async login(username = 'admin', password = 'admin'): Promise<boolean> {
    try {
      const res = await fetch(`${this.baseUrl}/api/login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ username, password }),
      })
      if (!res.ok) return false
      const text = await res.text()
      // FileBrowser returns the JWT as a plain string (possibly quoted)
      this.token = text.replace(/^"|"$/g, '')
      return true
    } catch {
      return false
    }
  }

  private headers(): Record<string, string> {
    const h: Record<string, string> = {}
    if (this.token) h['X-Auth'] = this.token
    return h
  }

  async listDirectory(path: string): Promise<FileBrowserItem[]> {
    const safePath = path.startsWith('/') ? path : `/${path}`
    const res = await fetch(`${this.baseUrl}/api/resources${safePath}`, {
      headers: this.headers(),
    })
    if (!res.ok) throw new Error(`Failed to list directory: ${res.status}`)
    const data: FileBrowserListResponse = await res.json()
    return (data.items || []).map((item) => ({
      ...item,
      extension: item.name.includes('.') ? item.name.split('.').pop()!.toLowerCase() : '',
    }))
  }

  downloadUrl(path: string): string {
    const safePath = path.startsWith('/') ? path : `/${path}`
    // Token is passed as query param for direct downloads (img src, audio src, etc.)
    return `${this.baseUrl}/api/raw${safePath}?auth=${this.token}`
  }

  async upload(dirPath: string, file: File): Promise<void> {
    const safePath = dirPath.endsWith('/') ? dirPath : `${dirPath}/`
    const encodedName = encodeURIComponent(file.name)
    const res = await fetch(
      `${this.baseUrl}/api/resources${safePath}${encodedName}?override=true`,
      {
        method: 'POST',
        headers: this.headers(),
        body: file,
      },
    )
    if (!res.ok) {
      const text = await res.text().catch(() => '')
      throw new Error(`Upload failed (${res.status}): ${text}`)
    }
  }

  async createFolder(parentPath: string, name: string): Promise<void> {
    const safePath = parentPath.endsWith('/') ? parentPath : `${parentPath}/`
    const res = await fetch(`${this.baseUrl}/api/resources${safePath}${name}/`, {
      method: 'POST',
      headers: this.headers(),
    })
    if (!res.ok) throw new Error(`Create folder failed: ${res.status}`)
  }

  async deleteItem(path: string): Promise<void> {
    const safePath = path.startsWith('/') ? path : `/${path}`
    const res = await fetch(`${this.baseUrl}/api/resources${safePath}`, {
      method: 'DELETE',
      headers: this.headers(),
    })
    if (!res.ok) throw new Error(`Delete failed: ${res.status}`)
  }

  async getUsage(): Promise<{ totalSize: number; folderCount: number; fileCount: number }> {
    if (!this.isAuthenticated) {
      const ok = await this.login()
      if (!ok) return { totalSize: 0, folderCount: 0, fileCount: 0 }
    }
    const res = await fetch(`${this.baseUrl}/api/resources/`, {
      headers: this.headers(),
    })
    if (!res.ok) return { totalSize: 0, folderCount: 0, fileCount: 0 }
    const data: FileBrowserListResponse = await res.json()
    const items = data.items || []
    const folderCount = items.filter(i => i.isDir).length
    const fileCount = items.filter(i => !i.isDir).length
    const totalSize = items.reduce((sum, i) => sum + (i.size || 0), 0)
    return { totalSize, folderCount, fileCount }
  }

  private static TEXT_EXTENSIONS = new Set([
    'txt', 'md', 'json', 'csv', 'log', 'conf', 'yaml', 'yml', 'toml', 'xml',
    'html', 'css', 'js', 'ts', 'py', 'sh', 'bash', 'env', 'ini', 'cfg',
    'sql', 'rs', 'go', 'java', 'c', 'h', 'cpp', 'hpp', 'rb', 'php',
    'dockerfile', 'makefile', 'gitignore', 'editorconfig',
  ])

  isTextFile(path: string): boolean {
    const ext = path.includes('.') ? path.split('.').pop()!.toLowerCase() : ''
    const name = path.split('/').pop()?.toLowerCase() || ''
    return FileBrowserClient.TEXT_EXTENSIONS.has(ext) || FileBrowserClient.TEXT_EXTENSIONS.has(name)
  }

  async readFileAsText(path: string, maxBytes = 102400): Promise<{ content: string; truncated: boolean; size: number }> {
    if (!this.isAuthenticated) {
      const ok = await this.login()
      if (!ok) throw new Error('FileBrowser authentication failed')
    }
    if (!this.isTextFile(path)) {
      throw new Error(`Cannot read binary file: ${path}`)
    }
    const safePath = path.startsWith('/') ? path : `/${path}`
    const res = await fetch(`${this.baseUrl}/api/raw${safePath}`, {
      headers: this.headers(),
    })
    if (!res.ok) throw new Error(`Failed to read file: ${res.status}`)
    const blob = await res.blob()
    const size = blob.size
    const truncated = size > maxBytes
    const slice = truncated ? blob.slice(0, maxBytes) : blob
    const content = await slice.text()
    return { content, truncated, size }
  }

  async rename(oldPath: string, newName: string): Promise<void> {
    const safePath = oldPath.startsWith('/') ? oldPath : `/${oldPath}`
    const dir = safePath.substring(0, safePath.lastIndexOf('/') + 1)
    const res = await fetch(`${this.baseUrl}/api/resources${safePath}`, {
      method: 'PATCH',
      headers: {
        ...this.headers(),
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ destination: `${dir}${newName}` }),
    })
    if (!res.ok) throw new Error(`Rename failed: ${res.status}`)
  }
}

export const fileBrowserClient = new FileBrowserClient()
