import React, { useEffect, useState } from "react";
import axios from "axios";
import { Button } from "@/prime-react";
import { Dialog } from "@/prime-react";
import { Dropdown } from "@/prime-react";
import { InputText } from "@/prime-react";
import { Message } from "@/prime-react";
import { ProgressSpinner } from "@/prime-react";
import { Calendar } from "@/prime-react";
import { Tag } from "@/prime-react";
import RefreshButton from "./shared/RefreshButton";

// State stays a LOCAL "YYYY-MM-DDTHH:MM" string so save() is unchanged; PrimeReact's Calendar
// speaks Date objects, so these convert between the two. Both directions are built from local
// parts on purpose — new Date("2026-08-19T10:00") is fine, but the naive date-only form
// new Date("2026-08-19") parses as UTC midnight and lands a day early in Hong Kong.
const pad = (n) => String(n).padStart(2, "0");
const toLocalInput = (d) =>
    `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
const fromLocalInput = (s) => {
    if (!s) return null;
    const [date, time = "00:00"] = s.split("T");
    const [y, m, d] = date.split("-").map(Number);
    const [hh, mm] = time.split(":").map(Number);
    return (y && m && d) ? new Date(y, m - 1, d, hh || 0, mm || 0) : null;
};

const STATUS_SEVERITY = {
    active:   "success",
    disabled: "warning",
    expired:  "danger",
};

const JoinLink = () => {
    const [links, setLinks] = useState([]);
    const [baseUrl, setBaseUrl] = useState("");
    const [loading, setLoading] = useState(true);

    const [createOpen, setCreateOpen] = useState(false);
    const [label, setLabel] = useState("");
    const [expiresAt, setExpiresAt] = useState("");   // "YYYY-MM-DDTHH:MM" or ""
    // Issuing a role-carrying link IS the grant, which is how a RESTRICTED role reaches someone
    // without a separate admin step — so restricted roles are offered here on purpose.
    const [userTypeId, setUserTypeId] = useState(null);
    const [serviceId, setServiceId] = useState(null);
    const [types, setTypes] = useState([]);
    const [services, setServices] = useState([]);
    const [creating, setCreating] = useState(false);
    const [createError, setCreateError] = useState(null);
    const [createInfo, setCreateInfo] = useState(null);

    const [confirmState, setConfirmState] = useState({
        visible: false,
        action: null,  // "regenerate" | "delete"
        id: null,
        running: false,
        error: null,
        info: null,
    });

    const [copyStatus, setCopyStatus] = useState({}); // id => "copied"

    const load = async () => {
        setLoading(true);
        try {
            const [res, t, sv] = await Promise.all([
                axios.get("/join_link/list"),
                axios.get("/user_type/list"),
                axios.get("/service/list"),
            ]);
            setLinks(res.data?.links || []);
            setBaseUrl(res.data?.base_url || "");
            setTypes((t.data || []).filter(x => x.enabled));
            setServices(sv.data || []);
        } catch (err) {
            console.error(err);
        } finally {
            setLoading(false);
        }
    };

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

    const fullUrl = (token) => `${baseUrl}/?token=${token}`;

    const openCreate = () => {
        setLabel("");
        setExpiresAt("");
        setUserTypeId(null);
        setServiceId(null);
        setCreateError(null);
        setCreateInfo(null);
        setCreateOpen(true);
    };

    // A service link must name the role the invitee joins as — service_participant records it and
    // the column is NOT NULL — and the role has to be one that service actually accepts.
    const serviceOptions = services.filter(sv => !userTypeId || (sv.user_type_ids || []).includes(userTypeId));
    const needsRole = Boolean(serviceId) && !userTypeId;

    const create = async () => {
        setCreating(true);
        setCreateError(null);
        try {
            await axios.post("/join_link/create", {
                label,
                user_type_id: userTypeId,
                service_id: serviceId,
                expires_at: expiresAt ? new Date(expiresAt).toISOString() : null,
            });
            setCreateInfo("Link created.");
            await load();
            setTimeout(() => setCreateOpen(false), 600);
        } catch (err) {
            setCreateError(err.response?.data?.message || "Could not create link.");
        } finally {
            setCreating(false);
        }
    };

    const toggle = async (id) => {
        try {
            await axios.post("/join_link/toggle", { id });
            await load();
        } catch (err) {
            console.error(err);
        }
    };

    const copy = async (id, url) => {
        try {
            await navigator.clipboard.writeText(url);
            setCopyStatus({ ...copyStatus, [id]: "copied" });
            setTimeout(() => {
                setCopyStatus(prev => {
                    const { [id]: _, ...rest } = prev;
                    return rest;
                });
            }, 1600);
        } catch {}
    };

    const openConfirm = (action, id) => {
        setConfirmState({ visible: true, action, id, running: false, error: null, info: null });
    };

    const closeConfirm = () => {
        if (confirmState.running) return;
        setConfirmState({ visible: false, action: null, id: null, running: false, error: null, info: null });
    };

    const runConfirm = async () => {
        const { action, id } = confirmState;
        const endpoint = action === "regenerate" ? "/join_link/regenerate" : "/join_link/delete";
        const successLabel = action === "regenerate" ? "New link generated. Share the new URL." : "Link deleted.";

        setConfirmState(s => ({ ...s, running: true, error: null, info: null }));
        try {
            await axios.post(endpoint, { id });
            setConfirmState(s => ({ ...s, running: false, info: successLabel }));
            await load();
            setTimeout(() => setConfirmState({ visible: false, action: null, id: null, running: false, error: null, info: null }), 700);
        } catch (err) {
            setConfirmState(s => ({
                ...s,
                running: false,
                error: err.response?.data?.message || "Action failed.",
            }));
        }
    };

    const confirmMeta = {
        regenerate: {
            header:  "Regenerate link",
            message: "Generate a new token for this link? The old URL will stop working immediately.",
            confirm: "Regenerate",
            icon:    "pi pi-refresh",
            danger:  false,
        },
        delete: {
            header:  "Delete link",
            message: "Permanently delete this link? Anyone holding it will no longer be able to join.",
            confirm: "Delete",
            icon:    "pi pi-trash",
            danger:  true,
        },
    };

    return (
        <div className="pt-fade-in">
            <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginBottom: 16 }}>
                <RefreshButton onClick={load} />
                <Button icon="pi pi-plus" label="New link" className="p-button-sm" onClick={openCreate} />
            </div>

            {loading ? (
                <div style={{ display: "flex", justifyContent: "center", padding: "60px 0" }}>
                    <ProgressSpinner style={{ width: "2.5rem", height: "2.5rem" }} />
                </div>
            ) : links.length === 0 ? (
                <div className="pt-card pt-card-pad" style={{ textAlign: "center", padding: "40px 24px", color: "var(--muted)" }}>
                    No join links yet. Click <b>New link</b> to create one.
                </div>
            ) : (
                <div className="pt-grid" style={{ gap: 16 }}>
                    {links.map(link => {
                        const url = fullUrl(link.token);
                        const isCopied = copyStatus[link.id] === "copied";
                        return (
                            <div key={link.id} className="pt-card pt-card-pad" style={{ display: "flex", flexDirection: "column", gap: 14 }}>
                                <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
                                    <div style={{ flex: 1, minWidth: 0 }}>
                                        <div style={{ fontSize: 15, fontWeight: 700, marginBottom: 4 }}>{link.label}</div>
                                        <div style={{ fontSize: 12, color: "var(--muted)" }}>
                                            {link.accepted_count} {link.accepted_count === 1 ? "join" : "joins"}
                                            {link.expires_at && ` · expires ${new Date(link.expires_at).toLocaleDateString("en-HK")}`}
                                        </div>
                                    </div>
                                    <Tag severity={STATUS_SEVERITY[link.status]} value={link.status} />
                                </div>

                                <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                                    <InputText
                                        value={url}
                                        readOnly
                                        title={isCopied ? "Copied to clipboard" : "Click to copy"}
                                        style={{ flex: 1, fontFamily: "var(--mono)", fontSize: 12.5, cursor: "pointer" }}
                                        onClick={(e) => { e.target.select(); copy(link.id, url); }}
                                    />
                                    <Button
                                        icon={isCopied ? "pi pi-check" : "pi pi-copy"}
                                        label={isCopied ? "Copied" : "Copy"}
                                        className="p-button-sm p-button-info"
                                        onClick={() => copy(link.id, url)} />
                                </div>

                                <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
                                    <Button
                                        icon={link.enabled ? "pi pi-pause" : "pi pi-play"}
                                        label={link.enabled ? "Disable" : "Enable"}
                                        className="p-button-sm p-button-secondary"
                                        onClick={() => toggle(link.id)} />
                                    <Button icon="pi pi-refresh" label="Regenerate" className="p-button-sm p-button-warning"
                                        onClick={() => openConfirm("regenerate", link.id)} />
                                    <Button icon="pi pi-trash" label="Delete" className="p-button-sm p-button-danger"
                                        onClick={() => openConfirm("delete", link.id)} />
                                </div>
                            </div>
                        );
                    })}
                </div>
            )}

            <Dialog visible={createOpen} onHide={() => !creating && setCreateOpen(false)}
                closable={!creating} closeOnEscape={!creating}
                header="Create a join link" style={{ width: "28rem" }}>
                <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
                    <div>
                        <label htmlFor="jl-label" className="form-label">Label</label>
                        <InputText id="jl-label" value={label} onChange={(e) => setLabel(e.target.value)}
                            placeholder="e.g. Instagram bio" disabled={creating} autoFocus />
                    </div>
                    <div>
                        <label htmlFor="jl-role" className="form-label">Joins as (optional)</label>
                        <Dropdown id="jl-role" value={userTypeId} disabled={creating} style={{ width: "100%" }}
                            options={types} optionLabel="name" optionValue="id"
                            placeholder="No role — they join the organization only"
                            showClear onChange={(e) => { setUserTypeId(e.value ?? null); setServiceId(null); }} />
                        <small style={{ color: "var(--muted)" }}>
                            Whoever follows this link is granted that role on arrival. This is the way to
                            hand out a <b>restricted</b> role — issuing the link is the grant.
                        </small>
                    </div>
                    <div>
                        <label htmlFor="jl-service" className="form-label">Into a service (optional)</label>
                        <Dropdown id="jl-service" value={serviceId} disabled={creating || !userTypeId}
                            style={{ width: "100%" }}
                            options={serviceOptions} optionLabel="name" optionValue="id"
                            placeholder={userTypeId ? "Organization only" : "Pick a role first"}
                            showClear onChange={(e) => setServiceId(e.value ?? null)} />
                        <small style={{ color: "var(--muted)" }}>
                            {!userTypeId
                                ? "A service link has to say which role the invitee joins as, so pick one above first."
                                : (serviceOptions.length === 0
                                    ? "No service accepts that role — check the service's Join as list."
                                    : "They are enrolled on this service the moment they follow the link. This is how somebody reaches an invite-only service.")}
                        </small>
                    </div>
                    <div>
                        <label htmlFor="jl-expires" className="form-label">Expires (optional)</label>
                        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                            {/* Was <input type="datetime-local">, which renders the OPERATING
                                SYSTEM's picker — unstyleable browser chrome whose internal segment
                                padding never lines up with the fields beside it. Calendar is real
                                DOM, so org.scss dresses it (.p-datepicker) to match the kit. */}
                            <Calendar
                                id="jl-expires"
                                value={fromLocalInput(expiresAt)}
                                onChange={(e) => setExpiresAt(e.value ? toLocalInput(e.value) : "")}
                                showTime hourFormat="24" showIcon
                                dateFormat="yy-mm-dd"
                                // An expiry in the past would create a link that is dead on arrival.
                                minDate={new Date()}
                                placeholder="Never expires"
                                disabled={creating}
                                inputClassName="pt-input"
                                style={{ flex: 1 }}
                            />
                            {expiresAt && (
                                <Button type="button" label="Clear" className="p-button-sm p-button-secondary"
                                    onClick={() => setExpiresAt("")} disabled={creating} />
                            )}
                        </div>
                        <div style={{ fontSize: 12, color: "var(--muted)", marginTop: 6 }}>
                            Leave blank for no expiry. Uses your local timezone.
                        </div>
                    </div>
                    {createError && <Message severity="error" text={createError} />}
                    {createInfo && <Message severity="success" text={createInfo} />}
                    <div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
                        <Button label="Cancel" className="p-button-sm p-button-secondary"
                            onClick={() => setCreateOpen(false)} disabled={creating} />
                        <Button label={creating ? "Creating…" : "Create link"}
                            icon={creating ? null : "pi pi-plus"} loading={creating}
                            className="p-button-sm" onClick={create} disabled={creating || !label || needsRole} />
                    </div>
                </div>
            </Dialog>

            <Dialog visible={confirmState.visible} onHide={closeConfirm}
                closable={!confirmState.running} closeOnEscape={!confirmState.running}
                header={confirmState.action ? confirmMeta[confirmState.action].header : ""}
                style={{ width: "26rem" }}>
                {confirmState.action && (
                    <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
                        <p style={{ margin: 0, color: "var(--ink-2)", fontSize: 14, lineHeight: 1.55 }}>
                            {confirmMeta[confirmState.action].message}
                        </p>
                        {confirmState.error && <Message severity="error" text={confirmState.error} />}
                        {confirmState.info && <Message severity="success" text={confirmState.info} />}
                        <div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
                            <Button label="Cancel" className="p-button-sm p-button-secondary"
                                onClick={closeConfirm} disabled={confirmState.running} />
                            <Button label={confirmState.running ? "Working…" : confirmMeta[confirmState.action].confirm}
                                icon={confirmState.running ? null : confirmMeta[confirmState.action].icon}
                                loading={confirmState.running}
                                className={`p-button-sm ${confirmMeta[confirmState.action].danger ? "p-button-danger" : ""}`}
                                onClick={runConfirm} disabled={confirmState.running} />
                        </div>
                    </div>
                )}
            </Dialog>
        </div>
    );
};

export default JoinLink;
