import { API_URL, authInstance, Modal } from "@shared";
import { Button, CircularProgress, TextField } from "@mui/material";
import { useEffect, useState, useMemo } from "react";
import { toast } from "react-toastify";

interface DeviceLogChunk {
  date?: string;
  lines?: string[];
}

interface DeviceLogsModalProps {
  open: boolean;
  deviceUuid: string | null;
  onClose: () => void;
}

const toYYYYMMDD = (d: Date) => d.toISOString().slice(0, 10);
const shiftYYYYMMDD = (value: string, days: number) => {
  const d = new Date(`${value}T00:00:00Z`);
  if (Number.isNaN(d.getTime())) return value;
  d.setUTCDate(d.getUTCDate() + days);
  return toYYYYMMDD(d);
};

type LogLevel = "info" | "warn" | "error" | "debug" | "fatal" | "unknown";

const LOG_LEVEL_STYLES: Record<LogLevel, { badge: string; text: string }> = {
  info: {
    badge: "bg-blue-100 text-blue-700",
    text: "text-[#000000BF]",
  },
  debug: {
    badge: "bg-gray-100 text-gray-600",
    text: "text-gray-600",
  },
  warn: {
    badge: "bg-amber-100 text-amber-700",
    text: "text-amber-800",
  },
  error: {
    badge: "bg-red-100 text-red-700",
    text: "text-red-700",
  },
  fatal: {
    badge: "bg-red-200 text-red-900",
    text: "text-red-900 font-semibold",
  },
  unknown: {
    badge: "bg-gray-100 text-gray-500",
    text: "text-[#000000BF]",
  },
};

const formatTs = (raw: string): string => {
  try {
    const d = new Date(raw);
    if (Number.isNaN(d.getTime())) return raw;
    const pad = (n: number) => String(n).padStart(2, "0");
    return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
  } catch {
    return raw;
  }
};

const parseJsonLogLine = (line: string) => {
  try {
    const obj = JSON.parse(line);
    if (obj && typeof obj === "object") {
      const level: LogLevel =
        obj.level && obj.level in LOG_LEVEL_STYLES
          ? (obj.level as LogLevel)
          : "unknown";
      const ts: string = obj.ts ? formatTs(obj.ts) : "";
      const msg: string = obj.msg ?? "";

      const extra: Record<string, unknown> = { ...obj };
      delete extra.level;
      delete extra.ts;
      delete extra.msg;
      delete extra.caller;
      const extraStr = Object.keys(extra).length
        ? " " +
          Object.entries(extra)
            .map(([k, v]) => `${k}=${typeof v === "string" ? v : JSON.stringify(v)}`)
            .join(" ")
        : "";

      return { ts, level, msg, extraStr };
    }
  } catch {
    return null;
  }
  return null;
};

const parseLogLine = (line: string, index: number) => {
  const json = parseJsonLogLine(line);
  if (json) {
    return {
      id: index,
      time: json.ts,
      text: json.msg + json.extraStr,
      level: json.level,
      sortKey: json.ts,
    };
  }

  const bracketMatch = line.match(/^(\[[^\]]+\])\s*(.*)$/);
  if (bracketMatch) {
    const rawTime = bracketMatch[1].replace(/^\[|\]$/g, "").trim();
    return {
      id: index,
      time: formatTs(rawTime),
      text: bracketMatch[2].trim() || line,
      level: "unknown" as LogLevel,
      sortKey: rawTime,
    };
  }

  return {
    id: index,
    time: "",
    text: line,
    level: "unknown" as LogLevel,
    sortKey: "",
  };
};

export const DeviceLogsModal = ({
  open,
  deviceUuid,
  onClose,
}: DeviceLogsModalProps) => {
  const [chunks, setChunks] = useState<DeviceLogChunk[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const today = new Date();
  const yesterday = new Date(today.getTime() - 24 * 60 * 60 * 1000);
  const [dateFrom, setDateFrom] = useState(toYYYYMMDD(yesterday));
  const [dateTo, setDateTo] = useState(toYYYYMMDD(today));
  const dateToMin = shiftYYYYMMDD(dateFrom, 1);
  const dateFromMax = shiftYYYYMMDD(dateTo, -1);

  const handleDateFromChange = (value: string) => {
    setDateFrom(value);
    if (!dateTo || dateTo <= value) {
      setDateTo(shiftYYYYMMDD(value, 1));
    }
  };

  const handleDateToChange = (value: string) => {
    if (value <= dateFrom) {
      toast.info("Дата 'До' должна быть позже даты 'От'");
      setDateTo(shiftYYYYMMDD(dateFrom, 1));
      return;
    }
    setDateTo(value);
  };

  useEffect(() => {
    if (!open || !deviceUuid) return;

    const fetchLogs = async () => {
      setIsLoading(true);
      setError(null);
      try {
        const { data } = await authInstance.get<DeviceLogChunk[]>(
          `${API_URL}/devices/${deviceUuid}/logs`,
          {
            params: {
              from: dateFrom,
              to: toYYYYMMDD(new Date(new Date(dateTo).getTime() + 86400000)),
            },
          }
        );
        setChunks(Array.isArray(data) ? data : []);
      } catch (err: unknown) {
        const msg =
          err && typeof err === "object" && "message" in err
            ? String((err as { message?: string }).message)
            : "Ошибка загрузки логов";
        setError(msg);
        toast.error(msg);
      } finally {
        setIsLoading(false);
      }
    };

    fetchLogs();
  }, [open, deviceUuid, dateFrom, dateTo]);

  const logs = useMemo(() => {
    const parsed = chunks.flatMap((chunk, chunkIdx) =>
      (chunk.lines ?? []).map((line, i) =>
        parseLogLine(line, chunkIdx * 10000 + i)
      )
    );
    parsed.sort((a, b) => {
      if (!a.sortKey && !b.sortKey) return 0;
      if (!a.sortKey) return 1;
      if (!b.sortKey) return -1;
      return b.sortKey.localeCompare(a.sortKey);
    });
    return parsed;
  }, [chunks]);

  const logsText = useMemo(
    () =>
      logs
        .map((log) => {
          const level = log.level === "unknown" ? "LOG" : log.level.toUpperCase();
          const time = log.time ? `[${log.time}] ` : "";
          return `${time}${level}: ${log.text}`;
        })
        .join("\n"),
    [logs]
  );

  const handleDownloadLogs = () => {
    if (!logsText) {
      toast.info("Нет логов для сохранения");
      return;
    }

    try {
      const safeDeviceUuid = (deviceUuid ?? "device").replace(
        /[^a-zA-Z0-9_-]/g,
        "_"
      );
      const fileName = `logs_${safeDeviceUuid}_${dateFrom}_${dateTo}.txt`;
      const blob = new Blob([`\uFEFF${logsText}`], {
        type: "text/plain;charset=utf-8",
      });
      const url = URL.createObjectURL(blob);
      const link = document.createElement("a");

      link.href = url;
      link.download = fileName;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      URL.revokeObjectURL(url);

      toast.success("Логи сохранены в .txt");
    } catch {
      toast.error("Не удалось сохранить логи");
    }
  };

  return (
    <Modal open={open} onClose={onClose} sx={{ width: "80vw", p: 3 }}>
      <div className="flex flex-col gap-6 h-[85vh]">
        <div className="flex gap-4 items-center justify-between w-full flex-wrap">
          <h2 className="text-2xl font-semibold text-[#000000BF]">Логи</h2>
          <div className="flex gap-4 items-center">
            <TextField
              type="date"
              label="От"
              size="small"
              value={dateFrom}
              onChange={(e) => handleDateFromChange(e.target.value)}
              slotProps={{
                inputLabel: { shrink: true },
                htmlInput: { max: dateFromMax },
              }}
            />
            <TextField
              type="date"
              label="До"
              size="small"
              value={dateTo}
              onChange={(e) => handleDateToChange(e.target.value)}
              slotProps={{
                inputLabel: { shrink: true },
                htmlInput: { min: dateToMin },
              }}
            />
            <Button
              variant="outlined"
              size="small"
              onClick={handleDownloadLogs}
              disabled={isLoading || Boolean(error) || logs.length === 0}
            >
              Скачать .txt
            </Button>
          </div>
        </div>

        <div className="flex-1 flex flex-col min-h-0 w-full">
          {isLoading && (
            <div className="w-full h-full flex items-center justify-center">
              <CircularProgress />
            </div>
          )}

          {!isLoading && error && (
            <div className="w-full h-full flex items-center justify-center text-red-500">
              {error}
            </div>
          )}

          {!isLoading && !error && (
            <div className="w-full h-full overflow-y-auto rounded-xl">
              <div className="flex flex-col gap-0.5 font-mono text-[13px]">
                {logs.length > 0 ? (
                  logs.map((log) => {
                    const style = LOG_LEVEL_STYLES[log.level];
                    return (
                      <div
                        key={log.id}
                        className={`flex gap-3 items-start px-2 py-1 rounded ${style.text}`}
                      >
                        <span
                          className={`shrink-0 px-1.5 py-0.5 rounded text-[11px] font-semibold uppercase ${style.badge}`}
                        >
                          {log.level === "unknown" ? "LOG" : log.level}
                        </span>
                        <span className="text-gray-400 shrink-0 whitespace-nowrap">
                          {log.time || null}
                        </span>
                        <span className="break-all">{log.text}</span>
                      </div>
                    );
                  })
                ) : (
                  <div className="h-full flex items-center justify-center text-gray-500 py-10">
                    Логи отсутствуют.
                  </div>
                )}
              </div>
            </div>
          )}
        </div>
      </div>
    </Modal>
  );
};
