/*
 * Copyright 2026 Eric Amell
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* Versage Console — Git credentials (org-admin). Register a Git provider token so the
   server can read commit history — this is what powers the "preview next bump" queries
   and future automation. The token is write-only: it's never returned by the API, at
   creation or otherwise. upsertGitCredential ALWAYS inserts a new row, so "edit" here is
   implemented as delete-then-create. */
const { Card, Input, Select, Button, Callout, Badge, Avatar, IconButton, Tooltip, Dialog } = window.VersageDesignSystem_c978e9;
const { fmtDate, relTime } = window.VSG;

const GC_CSS = `
.gc { padding: var(--d-pad); max-width: 920px; }
.gc__h { font-family:var(--font-display); font-size:20px; font-weight:600; letter-spacing:-0.01em; }
.gc__sub { font-size:13px; color:var(--text-muted); margin-top:4px; max-width:64ch; line-height:1.5; }
.gc__bar { display:flex; align-items:center; justify-content:space-between; gap:12px; margin:22px 0 12px; }
.gc__barlabel { font-family:var(--font-mono); font-size:10.5px; letter-spacing:0.07em; text-transform:uppercase; color:var(--text-faint); }
.gctbl { width:100%; border-collapse:collapse; }
.gctbl th { text-align:left; font-family:var(--font-mono); font-size:10px; letter-spacing:0.08em; text-transform:uppercase; color:var(--text-faint); font-weight:500; padding:0 12px 9px; border-bottom:1px solid var(--border-subtle); white-space:nowrap; }
.gctbl td { padding:13px 12px; font-size:var(--d-fs); vertical-align:middle; border-bottom:1px solid var(--border-subtle); }
.gctbl tr:last-child td { border-bottom:none; }
.gc__prov { display:flex; align-items:center; gap:11px; }
.gc__provic { width:34px; height:34px; border-radius:var(--radius-md); display:grid; place-items:center; background:var(--surface-3); border:1px solid var(--border-default); color:var(--text-secondary); flex:none; }
.gc__provnm { font-weight:600; display:flex; align-items:center; gap:8px; }
.gc__provhost { font-size:11.5px; color:var(--text-faint); font-family:var(--font-mono); margin-top:1px; }
.gc__owner { font-family:var(--font-mono); font-size:12px; }
.gc__acts { display:flex; align-items:center; gap:8px; justify-content:flex-end; }
.gc__empty { padding:40px 20px; text-align:center; color:var(--text-faint); border:1px dashed var(--border-default); border-radius:var(--radius-lg); }
.gc__empty__h { font-family:var(--font-display); font-size:16px; color:var(--text-secondary); margin-bottom:5px; }
.gc__field + .gc__field { margin-top:14px; }
`;
if (!document.getElementById("agc-css")) { const s=document.createElement("style"); s.id="agc-css"; s.textContent=GC_CSS; document.head.appendChild(s); }

const PROVIDER_META = {
  GITHUB:             { label:"GitHub",             host:"github.com", icon:IconGithub,    needsBaseUrl:false },
  GITLAB_COM:         { label:"GitLab",             host:"gitlab.com", icon:IconGitBranch, needsBaseUrl:false },
  GITLAB_SELF_HOSTED: { label:"GitLab self-hosted", host:null,         icon:IconGitBranch, needsBaseUrl:true  },
};
const PROVIDER_ORDER = ["GITHUB", "GITLAB_COM", "GITLAB_SELF_HOSTED"];

function providerHost(cred) {
  const m = PROVIDER_META[cred.providerType] || {};
  if (m.needsBaseUrl) return (cred.baseUrl || "").replace(/^https?:\/\//, "") || "—";
  return m.host || "—";
}

function GitCredentialDialog({ open, editing, onClose, onSubmit, busy }) {
  const [providerType, setProviderType] = React.useState("GITHUB");
  const [baseUrl, setBaseUrl] = React.useState("");
  const [ownerPattern, setOwnerPattern] = React.useState("");
  const [label, setLabel] = React.useState("");
  const [token, setToken] = React.useState("");

  React.useEffect(() => {
    if (!open) return;
    setProviderType(editing ? editing.providerType : "GITHUB");
    setBaseUrl(editing ? (editing.baseUrl || "") : "");
    setOwnerPattern(editing ? (editing.ownerPattern || "") : "");
    setLabel(editing ? (editing.label || "") : "");
    setToken("");
  }, [open, editing]);

  const meta = PROVIDER_META[providerType] || {};
  const needsBaseUrl = !!meta.needsBaseUrl;
  const valid = token.trim() && (!needsBaseUrl || baseUrl.trim());

  const submit = () => {
    if (!valid) return;
    onSubmit({
      providerType,
      baseUrl: needsBaseUrl ? baseUrl.trim() : null,
      ownerPattern: ownerPattern.trim() || null,
      label: label.trim() || null,
      token: token.trim(),
    });
  };

  return (
    <Dialog
      open={open}
      title={editing ? "Replace git credential" : "New git credential"}
      description={editing
        ? "Editing replaces the credential — the token can't be edited in place, so re-enter it."
        : "Register a provider token so Versage can read commit history for bump previews."}
      onClose={onClose}
      footer={<>
        <Button variant="ghost" onClick={onClose}>Cancel</Button>
        <Button variant="primary" disabled={!valid || busy} onClick={submit}>
          {busy ? "Saving…" : editing ? "Replace credential" : "Add credential"}
        </Button>
      </>}
    >
      <div className="gc__field">
        <Select label="Provider" value={providerType} onChange={e=>setProviderType(e.target.value)}
          options={PROVIDER_ORDER.map(p => ({ value:p, label:PROVIDER_META[p].label }))} />
      </div>

      {needsBaseUrl && (
        <div className="gc__field">
          <Input label="Instance URL" value={baseUrl} onChange={e=>setBaseUrl(e.target.value)} mono
            placeholder="https://gitlab.acme.dev" hint="Required for self-hosted GitLab — identifies which instance this token belongs to." />
        </div>
      )}

      <div className="gc__field">
        <Input label="Owner pattern" value={ownerPattern} onChange={e=>setOwnerPattern(e.target.value)} mono
          placeholder="acme-corp" hint="The org / user / namespace this credential covers. Leave blank to make it the default for this provider." />
      </div>

      <div className="gc__field">
        <Input label="Label" value={label} onChange={e=>setLabel(e.target.value)}
          placeholder="e.g. Acme GitHub bot" hint="Optional — shown in this list and in error messages." />
      </div>

      <div className="gc__field">
        <Input label="Token" type="password" value={token} onChange={e=>setToken(e.target.value)} mono
          placeholder="ghp_… / glpat-…" autoComplete="off"
          hint="Stored encrypted and never shown again. Needs read access to commit history." />
      </div>
    </Dialog>
  );
}

function AppGitCredentialsView({ org, pushToast }) {
  const { useIdentity, DeniedState } = window.RBAC;
  const { caps, orgRole } = useIdentity();
  const { useGitCredentials, upsertGitCredential, deleteGitCredential, DataLoading, DataError } = window.VSGData;

  const { data: creds, fetching, error, refetch } = useGitCredentials(org && org.slug);
  const [dialog, setDialog] = React.useState(null); // { editing? }
  const [del, setDel] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  if (!caps.orgManage) {
    return <div className="gc"><DeniedState title="Git credentials are admin-only" currentRole={orgRole} roleKind="org">
      Only organization admins can manage the provider tokens Versage uses to read commit history. Ask an admin if you need a change here.
    </DeniedState></div>;
  }

  const submit = async (input) => {
    setBusy(true);
    const editing = dialog && dialog.editing;
    try {
      // upsert always inserts → an edit is delete-old + create-new.
      if (editing) await deleteGitCredential(org.slug, editing.id);
      await upsertGitCredential(org.slug, input);
      pushToast && pushToast({ tone:"success", title: editing ? "Credential replaced" : "Credential added",
        msg:`${PROVIDER_META[input.providerType].label}${input.ownerPattern ? ` · ${input.ownerPattern}` : " · default"} is ready.` });
      setDialog(null);
    } catch (e) {
      pushToast && pushToast({ tone:"danger", title:"Couldn't save credential", msg:String(e.message||e) });
    } finally { setBusy(false); }
  };

  const doDelete = async () => {
    const c = del; setDel(null);
    try {
      await deleteGitCredential(org.slug, c.id);
      pushToast && pushToast({ tone:"info", title:"Credential removed", msg:`${PROVIDER_META[c.providerType].label}${c.ownerPattern ? ` · ${c.ownerPattern}` : " · default"} was deleted.` });
    } catch (e) {
      pushToast && pushToast({ tone:"danger", title:"Delete failed", msg:String(e.message||e) });
    }
  };

  return (
    <div className="gc">
      <div className="gc__h">Git credentials</div>
      <div className="gc__sub">
        Provider tokens let Versage read commit history from your repos — that's what powers the
        “preview next bump” for {org ? org.name : "this org"}. Tokens are write-only: once saved, they're never shown again.
      </div>

      <div style={{marginTop:16}}>
        <Callout tone="info" title="How credentials resolve">
          A credential with an <b>owner pattern</b> covers repos under that org / user / namespace. A credential with
          <b> no pattern</b> is the default for its provider. An exact pattern match wins over the default.
        </Callout>
      </div>

      <div className="gc__bar">
        <span className="gc__barlabel">{creds ? `${creds.length} credential${creds.length===1?"":"s"}` : "Credentials"}</span>
        <Button variant="primary" size="sm" iconLeft={<IconPlus size={15}/>} onClick={()=>setDialog({ editing:null })}>New credential</Button>
      </div>

      {error ? (
        /unauthor|forbidden|permission|not allowed|admin|sign|denied/i.test(error) ? (
          <Callout tone="warning" title="Org-admin sign-in required">
            <div style={{lineHeight:1.5}}>Managing git credentials needs an authenticated org admin. Sign in as an admin of {org ? org.name : "this org"} to view and edit them.</div>
            <div style={{marginTop:11}}><Button size="sm" variant="outline" onClick={refetch}>Retry</Button></div>
          </Callout>
        ) : (
          <DataError error={error} onRetry={refetch} />
        )
      ) : fetching && !creds ? (
        <DataLoading label="Loading credentials…" />
      ) : !creds || creds.length === 0 ? (
        <div className="gc__empty">
          <div className="gc__empty__h">No git credentials yet</div>
          <div style={{marginBottom:16}}>Add a provider token to unlock commit-based bump previews.</div>
          <Button variant="outline" size="sm" iconLeft={<IconPlus size={15}/>} onClick={()=>setDialog({ editing:null })}>Add your first credential</Button>
        </div>
      ) : (
        <table className="gctbl">
          <thead><tr><th>Provider</th><th>Owner pattern</th><th>Added</th><th></th></tr></thead>
          <tbody>
            {creds.map(c => {
              const m = PROVIDER_META[c.providerType] || {};
              const Ic = m.icon || IconKey;
              return (
                <tr key={c.id}>
                  <td>
                    <div className="gc__prov">
                      <div className="gc__provic"><Ic size={18}/></div>
                      <div>
                        <div className="gc__provnm">{c.label || m.label || c.providerType}</div>
                        <div className="gc__provhost">{providerHost(c)}</div>
                      </div>
                    </div>
                  </td>
                  <td>
                    {c.ownerPattern
                      ? <span className="gc__owner">{c.ownerPattern}</span>
                      : <Badge tone="brand">Default</Badge>}
                  </td>
                  <td><span style={{color:'var(--text-muted)'}}>{fmtDate(c.createdAt)} · {relTime(c.createdAt)}</span></td>
                  <td>
                    <div className="gc__acts">
                      <Button size="sm" variant="outline" onClick={()=>setDialog({ editing:c })}>Replace</Button>
                      <Tooltip label="Delete credential"><IconButton label="Delete credential" onClick={()=>setDel(c)}><IconTrash size={16}/></IconButton></Tooltip>
                    </div>
                  </td>
                </tr>
              );
            })}
          </tbody>
        </table>
      )}

      <GitCredentialDialog open={!!dialog} editing={dialog && dialog.editing} onClose={()=>setDialog(null)} onSubmit={submit} busy={busy} />

      <Dialog open={!!del} title="Delete git credential?"
        description={del ? `${PROVIDER_META[del.providerType].label}${del.ownerPattern ? ` · ${del.ownerPattern}` : " · default"} will be removed.` : ""}
        onClose={()=>setDel(null)}
        footer={<><Button variant="ghost" onClick={()=>setDel(null)}>Cancel</Button><Button variant="danger" onClick={doDelete}>Delete credential</Button></>}>
        <Callout tone="warning" title="Bump previews may stop working">
          Any repo that relied on this credential to read commit history will fall back to another matching credential, or fail until you add a new one.
        </Callout>
      </Dialog>
    </div>
  );
}
Object.assign(window, { AppGitCredentialsView });
