// Components related to XRPL Amendment previews and statuses

import * as React from 'react'
import { Link } from '@redocly/theme/components/Link/Link'
import { useThemeHooks } from '@redocly/theme/core/hooks'

type Amendment = {
  name: string
  rippled_version: string
  tx_hash?: string
  consensus?: string
  date?: string
  id: string
  eta?: string
}

type AmendmentsResponse = {
  amendments: Amendment[]
}

type AmendmentsCachePayload = {
  timestamp: number
  amendments: Amendment[]
}

// API data caching
const amendmentsEndpoint = 'https://vhs.prod.ripplex.io/v1/network/amendments/vote/main/'
const amendmentsInfoEndpoint = 'https://vhs.prod.ripplex.io/v1/network/amendments/info/main/'
const amendmentsCacheKey = 'xrpl.amendments.mainnet.cache'
const amendmentsTTL = 15 * 60 * 1000 // 15 minutes in milliseconds

function readAmendmentsCache(): Amendment[] | null {
  if (typeof window === 'undefined') return null
  try {
    const raw = window.localStorage.getItem(amendmentsCacheKey)
    if (!raw) return null
    const parsed: AmendmentsCachePayload = JSON.parse(raw)
    if (!parsed || !Array.isArray(parsed.amendments)) return null
    const fresh = Date.now() - parsed.timestamp < amendmentsTTL
    return fresh ? parsed.amendments : null
  } catch {
    return null
  }
}

function writeAmendmentsCache(amendments: Amendment[]) {
  if (typeof window === 'undefined') return
  try {
    const payload: AmendmentsCachePayload = { timestamp: Date.now(), amendments }
    window.localStorage.setItem(amendmentsCacheKey, JSON.stringify(payload))
  } catch {
    // Ignore quota or serialization errors
  }
}

// Sort amendments table by status, then chronologically, then alphabetically
function sortAmendments(list: Amendment[]): Amendment[] {
  const getStatusPriority = (amendment: Amendment): number => {
    if (amendment.consensus) return 0    // Open for Voting
    if (amendment.eta) return 1          // Expected
    if (amendment.tx_hash) return 2      // Enabled
    return 3                             // Fallback
  }

  const getChronoKey = (amendment: Amendment): number => {
    const raw = amendment.date || amendment.eta
    if (!raw) return 0
    const t = Date.parse(raw)
    return isNaN(t) ? 0 : t
  }

  return [...list].sort((a, b) => {
    // 1. Status
    const statusDiff = getStatusPriority(a) - getStatusPriority(b)
    if (statusDiff !== 0) return statusDiff

    // 2. Chronological (most recent first)
    const chronoA = getChronoKey(a)
    const chronoB = getChronoKey(b)
    if (chronoA !== chronoB) return chronoB - chronoA

    // 3. Alphabetical
    return a.name.localeCompare(b.name)
  })
}

// Generate amendments table with live mainnet data
export function AmendmentsTable() {
  const [amendments, setAmendments] = React.useState<Amendment[]>([])
  const [loading, setLoading] = React.useState(true)
  const [error, setError] = React.useState<string | null>(null)
  const { useTranslate } = useThemeHooks()
  const { translate } = useTranslate()

  React.useEffect(() => {
    const fetchAmendments = async () => {
      try {
        setLoading(true)
        // 1. Try cache first
        const cached = readAmendmentsCache()

        if (cached) {
          setAmendments(sortAmendments(cached))
          return // Use current cache (fresh)
        }
        // 2. Fetch new data if cache is stale
        const response = await fetch(amendmentsEndpoint)
        
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`)
        }

        const data: AmendmentsResponse = await response.json()
        writeAmendmentsCache(data.amendments)

        setAmendments(sortAmendments(data.amendments))
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Failed to fetch amendments')
      } finally {
        setLoading(false)
      }
    }

    fetchAmendments()
  }, [])

  // Fancy schmancy loading icon
  if (loading) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', padding: '2rem' }}>
        <div className="spinner-border text-primary" role="status">
          <span className="sr-only">{translate("amendment.loading", "Loading amendments...")}</span>
        </div>
        <div style={{ marginTop: '1rem' }}>{translate("amendment.loading", "Loading amendments...")}</div>
      </div>
    )
  }

  if (error) {
    return (
      <div className="alert alert-danger" role="alert">
        <strong>{translate("amendment.error", "Error loading amendments:")}:</strong> {error}
      </div>
    )
  }

  // Render amendment table
  return (
    <div className="md-table-wrapper">
      <table className="md">
        <thead>
          <tr>
            <th align="left" data-label="Name">{translate("amendment.table.name", "Name")}</th>
            <th align="left" data-label="Introduced">{translate("amendment.table.introduced", "Introduced")}</th>
            <th align="left" data-label="Status">{translate("amendment.table.status", "Status")}</th>
          </tr>
        </thead>
        <tbody>
          {amendments.map((amendment) => (
            <tr key={amendment.id}>
              <td align="left">
                <Link to={`#${amendment.name.toLowerCase()}`}>{amendment.name}</Link>
              </td>
              <td align="left">{amendment.rippled_version}</td>
              <td align="left">
                <AmendmentBadge amendment={amendment} />
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  )
}

function AmendmentBadge(props: { amendment: Amendment }) {
  const [status, setStatus] = React.useState<string>('Loading...')
  const [href, setHref] = React.useState<string | undefined>(undefined)
  const [color, setColor] = React.useState<string>('blue')
  const { useTranslate } = useThemeHooks()
  const { translate } = useTranslate()

  const enabledLabel = translate("amendment.status.enabled", "Enabled")
  const votingLabel = translate("amendment.status.openForVoting", "Open for Voting")
  const etaLabel = translate("amendment.status.eta", "Expected")
  const inactiveLabel = translate("amendment.status.inactive", "Inactive")
  const inactiveButton = translate("amendment.status.inactiveButton", "Get details")

  React.useEffect(() => {
    const amendment = props.amendment
    
    // Check if amendment is enabled (has tx_hash)
    if (amendment.tx_hash) {
      const enabledDate = new Date(amendment.date).toISOString().split('T')[0]
      setStatus(`${enabledLabel}: ${enabledDate}`)
      setColor('green')
      setHref(`https://livenet.xrpl.org/transactions/${amendment.tx_hash}`)
    } 
    // Check if expected activation is provided (has eta field)
    else if (amendment.eta) {
      let etaDate = new Date(amendment.eta).toISOString().split('T')[0]
      setStatus(`${etaLabel}: ${etaDate}`)
      setColor('blue')
      setHref(undefined)
    }
    // Check if amendment is currently being voted on (has consensus field)
    else if (amendment.consensus) {
      setStatus(`${votingLabel}: ${amendment.consensus}`)
      setColor('80d0e0')
      setHref(undefined)
    }
    // Fallback: amendment is inactive
    else {
      setStatus(`${inactiveLabel}: ${inactiveButton}`)
      setColor('lightgrey')
      setHref(`/resources/known-amendments#${amendment.name.toLowerCase()}`)
    }
  }, [props.amendment, enabledLabel, etaLabel, votingLabel, inactiveLabel])
  
  // Split the status at the colon to create two-color badge
  const parts = status.split(':')
  const label = shieldsIoEscape(parts[0])
  const message = shieldsIoEscape(parts.slice(1).join(':'))
  
  const badgeUrl = `https://img.shields.io/badge/${label}-${message}-${color}`

  if (href) {
    return (
      <Link to={href} target="_blank">
        <img src={badgeUrl} alt={status} className="shield" />
      </Link>
    )
  }

  return <img src={badgeUrl} alt={status} className="shield" />
}

export function AmendmentDisclaimer(props: {
  name: string,
  compact: boolean,
  mode: string
}) {
  const [amendmentStatus, setStatus] = React.useState<Amendment | null>(null)
  const [loading, setLoading] = React.useState(true)
  const [error, setError] = React.useState<string | null>(null)
  const { useTranslate } = useThemeHooks()
  const { translate } = useTranslate()

  const link = () => <Link to={`/resources/known-amendments#${props.name.toLowerCase()}`}>{props.name}{ props.compact ? "" : " amendment"}</Link>

  React.useEffect(() => {
    const loadAmendment = async () => {
      try {
        setLoading(true)
        // 1. Try cache first
        const cached = readAmendmentsCache()

        if (cached) {
          const found = cached.find(a => a.name === props.name)
            if (found) {
              setStatus(found)
              return // amendment successfully found in cache
            }
        }
        // 2. New API request for stale/missing cache. 
        // Also catches edge case of new amendment appearing
        // on mainnet within cache TTL window.
        const response = await fetch(amendmentsEndpoint)

        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`)
        }

        const data: AmendmentsResponse = await response.json()
        writeAmendmentsCache(data.amendments)

        const found = data.amendments.find(a => a.name === props.name)
        
        // 3. If not found in live data, try the info endpoint.
        if (!found) {
          
          const infoResponse = await fetch(amendmentsInfoEndpoint)
          
          if (!infoResponse.ok) {
            throw new Error(`HTTP error from info endpoint! Status: ${infoResponse.status}`)
          }
          
          const infoData: AmendmentsResponse = await infoResponse.json()
          const foundInInfo = infoData.amendments.find(a => a.name === props.name)
          
          if (!foundInInfo) {
            throw new Error(`Couldn't find ${props.name} amendment in status tables.`)
          }
          
          setStatus(foundInInfo)
          return
        }

        setStatus(found)
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Failed to fetch amendments')
      } finally {
        setLoading(false)
      }
    };
    loadAmendment()
  }, [props.name])

  if (loading) {
    return (
      <p><em>
      {translate("component.amendment-status.requires.1", "Requires the ")}{link()}{translate("component.amendment-status.requires.2", ".")}
      {" "}
      <span className="spinner-border text-primary" role="status">
        <span className="sr-only">{translate("amendment.loading_status", "Loading...")}</span>
      </span>
      </em></p>
    )
  }
  
  if (error) {
    return (
      <p><em>
      {translate("component.amendment-status.requires.1", "Requires the ")}{link()}{translate("component.amendment-status.requires.2", ".")}
      {" "}
      <span className="alert alert-danger" style={{display: "block"}}>
        <strong>{translate("amendment.error_status", "Error loading amendment status")}:</strong> {error}
      </span>
      </em></p>
    )
  }

  if (props.compact) {
    return (
      <>
        {link()}
        {" "}
        <AmendmentBadge amendment={amendmentStatus} />
      </>
    )
  }

  if (props.mode === "updated") {
    return (
      <p><em>(
        {
          amendmentStatus.date ? (
            <>
            {translate("component.amendment-status.updated.1", "Updated by the ")}{link()}
            {translate("component.amendment-status.updated.2", ".")}
            {" "}
            <AmendmentBadge amendment={amendmentStatus} />
            </>
          ) : (
            <>
            {translate("component.amendment-status.updates.1", "The ")}{link()}
            {translate("component.amendment-status.updates.2", " updates this.")}
            {" "}
            <AmendmentBadge amendment={amendmentStatus} />
            </>
          )
        }
      )</em></p>
    )
  }
  
  return (
    <p><em>(
      {
        amendmentStatus.date ? (
          <>
          {translate("component.amendment-status.added.1", "Added by the ")}{link()}
          {translate("component.amendment-status.added.2", ".")}
          {" "}
          <AmendmentBadge amendment={amendmentStatus} />
          </>
        ) : (
          <>
          {translate("component.amendment-status.requires.1", "Requires the ")}{link()}
          {translate("component.amendment-status.requires.2", ".")}
          {" "}
          <AmendmentBadge amendment={amendmentStatus} />
          </>
        )
      }
    )</em></p>
  )
}

function shieldsIoEscape(s: string) {
  return s.trim()
    .replace(/-/g, '--')
    .replace(/_/g, '__')
    .replace(/%/g, '%25')
}

export function Badge(props: {
    children: React.ReactNode
    color: string
    href: string
}) {
    const DEFAULT_COLORS = {
      "open for voting": "80d0e0",
      "投票中": "80d0e0", // ja: open for voting
      "expected": "blue",
      "予定": "blue", // ja: expected
      "enabled": "green",
      "有効": "green", // ja: enabled
      "obsolete": "red",
      "removed in": "red",
      "削除": "red", // ja: removed in
      "廃止": "red", // ja: obsolete
      "撤回": "red", // ja: withdrawn/removed/vetoed
      "new in": "blue",
      "新規": "blue", // ja: new in
      "updated in": "blue",
      "更新": "blue", // ja: updated in
      "in development": "lightgrey",
      "開発中": "lightgrey", // ja: in development
      "inactive": "lightgrey",
      "無効": "lightgrey" // ja: inactive
    }

    let childstrings = ""

    React.Children.forEach(props.children, (child, index) => {
      if (typeof child == "string") {
        childstrings += child
      }
    })

    const parts = childstrings.split(":")
    const left : string = shieldsIoEscape(parts[0])
    const right : string = shieldsIoEscape(parts.slice(1).join(":"))

    let color = props.color
    if (!color) {
      if (DEFAULT_COLORS.hasOwnProperty(left.toLowerCase())) {
        color = DEFAULT_COLORS[left.toLowerCase()]
      } else {
        color = "lightgrey"
      }
    }

    let badge_url = `https://img.shields.io/badge/${left}-${right}-${color}.svg`

    if (props.href) {
      return (
        <Link to={props.href}>
          <img src={badge_url} alt={childstrings} className="shield" />
        </Link>
      )
    } else {
      return (
        <img src={badge_url} alt={childstrings} className="shield" />
      )
    }
}
