import { useEffect, useRef, useState } from 'react';
import {
  Button, Callout, Intent, Tag, Spinner, NonIdealState,
  Collapse, HTMLTable,
} from '@blueprintjs/core';
import { getProjects, getBuildHistory, type ProjectDefinition, type BuildRecord } from '../api/buildApi';
import { getGitLog, type GitCommit } from '../api/gitApi';

const BASE_URL = import.meta.env.VITE_API_URL ?? '';

const KIND_INTENT: Record<string, Intent> = {
  DotnetProject:  Intent.PRIMARY,
  NpmProject:     Intent.WARNING,
  DockerImage:    Intent.NONE,
  SolutionBuild:  Intent.SUCCESS,
};

const STATUS_INTENT: Record<string, Intent> = {
  Succeeded: Intent.SUCCESS,
  Failed:    Intent.DANGER,
  Running:   Intent.PRIMARY,
};

// ── Git history panel ─────────────────────────────────────────────────────────

function GitHistoryPanel({ relativePath }: { relativePath: string }) {
  const [commits,  setCommits]  = useState<GitCommit[]>([]);
  const [loading,  setLoading]  = useState(false);
  const [open,     setOpen]     = useState(false);
  const [fetched,  setFetched]  = useState(false);
  const [error,    setError]    = useState<string | null>(null);

  const toggle = async () => {
    if (!open && !fetched) {
      setLoading(true);
      setError(null);
      try {
        const data = await getGitLog(relativePath, 10);
        setCommits(data);
        setFetched(true);
      } catch (e) {
        setError(e instanceof Error ? e.message : 'Failed to load git history');
      } finally {
        setLoading(false);
      }
    }
    setOpen((o) => !o);
  };

  return (
    <div style={{ marginTop: '0.5rem' }}>
      <Button
        minimal small
        icon="git-branch"
        text={open ? 'Hide history' : 'Git history'}
        onClick={toggle}
        loading={loading}
      />

      <Collapse isOpen={open && !loading}>
        {error && <Callout intent={Intent.DANGER} compact style={{ marginTop: '0.25rem' }}>{error}</Callout>}
        {commits.length === 0 && !error && (
          <p style={{ fontSize: '0.75rem', color: '#8f99a8', marginTop: '0.5rem' }}>No commits found for this path.</p>
        )}
        {commits.length > 0 && (
          <HTMLTable className="bp6-html-table-condensed bp6-html-table-striped" style={{ width: '100%', marginTop: '0.5rem', fontSize: '0.72rem' }}>
            <thead>
              <tr>
                <th style={{ width: 60 }}>Commit</th>
                <th>Message</th>
                <th>Author</th>
                <th style={{ width: 140 }}>Date</th>
                <th style={{ width: 40, textAlign: 'right' }}>Files</th>
              </tr>
            </thead>
            <tbody>
              {commits.map((c) => (
                <tr key={c.hash}>
                  <td>
                    <code style={{ fontSize: '0.7rem', color: '#4a90d9' }}>{c.shortHash}</code>
                  </td>
                  <td style={{ maxWidth: 320, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                    {c.subject}
                  </td>
                  <td style={{ color: '#738091' }}>{c.author}</td>
                  <td style={{ color: '#738091' }}>
                    {new Date(c.date).toLocaleString(undefined, { dateStyle: 'short', timeStyle: 'short' })}
                  </td>
                  <td style={{ textAlign: 'right', color: '#8f99a8' }}>
                    <span title={c.files.join('\n')}>{c.files.length}</span>
                  </td>
                </tr>
              ))}
            </tbody>
          </HTMLTable>
        )}
      </Collapse>
    </div>
  );
}

// ── Per-project card ──────────────────────────────────────────────────────────

function ProjectCard({
  project,
  lastBuild,
  onBuilt,
}: {
  project:   ProjectDefinition;
  lastBuild: BuildRecord | undefined;
  onBuilt:   () => void;
}) {
  const [building, setBuilding] = useState(false);
  const [logs,     setLogs]     = useState<string[]>([]);
  const [open,     setOpen]     = useState(false);
  const [error,    setError]    = useState<string | null>(null);
  const logRef                  = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight;
  }, [logs]);

  const handleBuild = async () => {
    setBuilding(true);
    setOpen(true);
    setLogs([]);
    setError(null);

    try {
      const res = await fetch(
        `${BASE_URL}/api/builds/${encodeURIComponent(project.name)}`,
        { method: 'POST' }
      );
      if (!res.ok || !res.body) throw new Error(res.statusText);

      const reader  = res.body.getReader();
      const decoder = new TextDecoder();
      let   buffer  = '';

      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        buffer += decoder.decode(value, { stream: true });
        const parts = buffer.split('\n\n');
        buffer = parts.pop() ?? '';
        for (const chunk of parts) {
          const dataLine = chunk.replace(/^data:\s*/m, '').trim();
          if (!dataLine) continue;
          try {
            const msg = JSON.parse(dataLine);
            if (msg.done) onBuilt();
            else if (typeof msg.line === 'string')
              setLogs((p) => [...p.slice(-1000), msg.line]);
          } catch { /* ignore */ }
        }
      }
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Unknown error');
    } finally {
      setBuilding(false);
    }
  };

  const statusIntent = lastBuild ? STATUS_INTENT[lastBuild.status] : Intent.NONE;
  const statusLabel  = lastBuild?.status ?? 'Never built';
  const lastRun      = lastBuild ? new Date(lastBuild.startedAt).toLocaleString() : '—';
  const duration     = lastBuild?.durationMs != null
    ? `${(lastBuild.durationMs / 1000).toFixed(1)}s` : null;

  return (
    <div className="job-card">
      <div className="job-card-header">
        <div>
          <strong>{project.name}</strong>
          <span className="job-card-subdomain" style={{ fontFamily: 'monospace', fontSize: '0.72rem' }}>
            {project.relativePath}
          </span>
        </div>
        <div style={{ display: 'flex', gap: '0.4rem', alignItems: 'center' }}>
          <Tag intent={KIND_INTENT[project.kind] ?? Intent.NONE} minimal round>{project.kind}</Tag>
          <Tag intent={statusIntent} round>{statusLabel}</Tag>
          {duration && <Tag minimal round>{duration}</Tag>}
        </div>
      </div>

      <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center', padding: '0.5rem 0 0.25rem' }}>
        <Button
          icon="play"
          small
          intent={Intent.PRIMARY}
          loading={building}
          onClick={handleBuild}
          text="Build"
        />
        <span style={{ fontSize: '0.75rem', color: '#999' }}>Last run: {lastRun}</span>
        {logs.length > 0 && (
          <Button minimal small
            icon={open ? 'chevron-up' : 'chevron-down'}
            onClick={() => setOpen((o) => !o)}
            text={open ? 'Hide log' : 'Show log'}
          />
        )}
      </div>

      {error && <Callout intent={Intent.DANGER} compact style={{ marginTop: '0.25rem' }}>{error}</Callout>}

      {open && logs.length > 0 && (
        <div
          ref={logRef}
          style={{
            fontFamily: 'monospace', fontSize: '0.7rem',
            background: '#111', color: '#d4d4d4',
            padding: '0.5rem 0.75rem', borderRadius: 4,
            height: 200, overflowY: 'auto',
            whiteSpace: 'pre-wrap', wordBreak: 'break-all',
            marginTop: '0.5rem',
          }}
        >
          {logs.map((l, i) => <div key={i}>{l}</div>)}
        </div>
      )}

      <GitHistoryPanel relativePath={project.relativePath} />
    </div>
  );
}

// ── Page ──────────────────────────────────────────────────────────────────────

export default function BuildMonitorPage() {
  const [projects, setProjects] = useState<ProjectDefinition[]>([]);
  const [history,  setHistory]  = useState<BuildRecord[]>([]);
  const [loading,  setLoading]  = useState(true);
  const [error,    setError]    = useState<string | null>(null);

  const load = async () => {
    setLoading(true);
    setError(null);
    try {
      const [p, h] = await Promise.all([getProjects(), getBuildHistory()]);
      setProjects(p);
      setHistory(h);
    } catch (e) {
      setError(e instanceof Error ? e.message : 'Failed to load');
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => { (async () => { await load(); })(); }, []);

  // Find latest build per project — match exactly by relativePath (= build target)
  const lastBuildFor = (project: ProjectDefinition): BuildRecord | undefined =>
    history.find((b) => b.target === project.relativePath);

  return (
    <>
      <div className="page-header">
        <div>
          <h1>Build Monitor</h1>
          <p>Trigger and track builds for every project in the solution.</p>
        </div>
        <div style={{ display: 'flex', gap: '0.5rem', alignItems: 'center' }}>
          <Button icon="refresh" minimal onClick={load} loading={loading} title="Refresh" />
          <Button
            icon="play"
            text="Build All"
            intent={Intent.WARNING}
            disabled={loading || projects.length === 0}
            onClick={async () => {
              for (const p of projects) {
                await fetch(`${BASE_URL}/api/builds/${encodeURIComponent(p.name)}`, { method: 'POST' });
              }
              load();
            }}
          />
        </div>
      </div>

      {error && (
        <Callout intent={Intent.DANGER} title="Failed to load projects" style={{ marginBottom: '1rem' }}>
          {error}
        </Callout>
      )}

      {loading && <NonIdealState icon={<Spinner />} title="Loading projects..." />}

      {!loading && !error && (
        <div className="job-list">
          {projects.map((p) => (
            <ProjectCard
              key={p.name}
              project={p}
              lastBuild={lastBuildFor(p)}
              onBuilt={load}
            />
          ))}
        </div>
      )}
    </>
  );
}
