import {
  MEDIA_TYPE_LABELS,
  MEDIA_TYPE_VALUES,
  editSightStore,
  generateDefaultMediaName,
  clearBlobAndGLTFCache,
  authStore,
  snapshotStore,
} from "@shared";
import { observer } from "mobx-react-lite";
import { useEffect, useState, useRef } from "react";
import { toast } from "react-toastify";
import {
  Dialog,
  DialogTitle,
  DialogContent,
  DialogActions,
  Button,
  TextField,
  Paper,
  Box,
  CircularProgress,
  Alert,
  Snackbar,
  Select,
  MenuItem,
  FormControl,
  InputLabel,
} from "@mui/material";
import { Save } from "lucide-react";
import { ModelViewer3D } from "@widgets";

interface UploadMediaDialogProps {
  open: boolean;
  onClose: () => void;
  afterUpload?: (media: {
    id: string;
    filename: string;
    media_name?: string;
    media_type: number;
  }) => void;
  afterUploadSight?: (id: string) => void;
  hardcodeType?:
    | "thumbnail"
    | "icon"
    | "alt_icon"
    | "watermark_lu"
    | "watermark_rd"
    | "image"
    | "video_preview"
    | null;
  contextObjectName?: string;
  contextType?:
    | "sight"
    | "city"
    | "carrier"
    | "country"
    | "vehicle"
    | "station"
    | "route"
    | "user";
  isArticle?: boolean;
  articleName?: string;
  initialFile?: File;
}

export const UploadMediaDialog = observer(
  ({
    open,
    onClose,
    afterUpload,
    afterUploadSight,
    hardcodeType,
    contextObjectName,

    isArticle,
    articleName,
    initialFile,
  }: UploadMediaDialogProps) => {
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState<string | null>(null);
    const [success, setSuccess] = useState(false);
    const [mediaName, setMediaName] = useState("");
    const [mediaFilename, setMediaFilename] = useState("");
    const [mediaType, setMediaType] = useState(0);
    const [mediaFile, setMediaFile] = useState<File | null>(null);
    const { fileToUpload, uploadMedia } = editSightStore;
    const [mediaUrl, setMediaUrl] = useState<string | null>(null);
    const [availableMediaTypes, setAvailableMediaTypes] = useState<number[]>(
      []
    );
    const [isPreviewLoaded, setIsPreviewLoaded] = useState(false);
    const previousMediaUrlRef = useRef<string | null>(null);

    useEffect(() => {
      if (initialFile) {
        if (
          previousMediaUrlRef.current &&
          previousMediaUrlRef.current.startsWith("blob:")
        ) {
          clearBlobAndGLTFCache(previousMediaUrlRef.current);
        }

        setMediaFile(initialFile);
        setMediaFilename(initialFile.name);

        const extension = initialFile.name.split(".").pop()?.toLowerCase();
        if (extension) {
          if (["glb", "gltf"].includes(extension)) {
            setAvailableMediaTypes([6]);
            setMediaType(6);
          }
          if (
            ["jpg", "jpeg", "png", "gif", "svg", "webp", "bmp"].includes(
              extension
            )
          ) {
            setAvailableMediaTypes([1, 3, 4, 5]);
            setMediaType(1);
          } else if (["mp4", "webm", "mov"].includes(extension)) {
            setAvailableMediaTypes([2]);
            setMediaType(2);
          }
        }

        const newBlobUrl = URL.createObjectURL(initialFile);
        setMediaUrl(newBlobUrl);
        previousMediaUrlRef.current = newBlobUrl;
        setMediaName(initialFile.name.replace(/\.[^/.]+$/, ""));
      }
    }, [initialFile]);

    useEffect(() => {
      return () => {
        if (
          previousMediaUrlRef.current &&
          previousMediaUrlRef.current.startsWith("blob:")
        ) {
          clearBlobAndGLTFCache(previousMediaUrlRef.current);
        }
      };
    }, []);

    useEffect(() => {
      if (fileToUpload) {
        setMediaFile(fileToUpload);
        setMediaFilename(fileToUpload.name);

        const extension = fileToUpload.name.split(".").pop()?.toLowerCase();
        if (extension) {
          if (["glb", "gltf"].includes(extension)) {
            setAvailableMediaTypes([6]);
            setMediaType(6);
          }
          if (
            ["jpg", "jpeg", "png", "gif", "svg", "webp", "bmp"].includes(
              extension
            )
          ) {
            setAvailableMediaTypes([1, 3, 4, 5]);
            setMediaType(1);
          } else if (["mp4", "webm", "mov"].includes(extension)) {
            setAvailableMediaTypes([2]);
            setMediaType(2);
          }
        }

        if (fileToUpload.name) {
          let defaultName = "";

          if (isArticle && articleName && contextObjectName) {
            defaultName = generateDefaultMediaName(
              contextObjectName,
              fileToUpload.name,
              articleName,
              true
            );
          } else if (contextObjectName && contextObjectName.trim() !== "") {
            const currentMediaType = hardcodeType
              ? MEDIA_TYPE_VALUES[hardcodeType]
              : 1;
            defaultName = generateDefaultMediaName(
              contextObjectName,
              fileToUpload.name,
              currentMediaType,
              false
            );
          } else {
            const currentMediaType = hardcodeType
              ? MEDIA_TYPE_VALUES[hardcodeType]
              : 1;
            defaultName = generateDefaultMediaName(
              "",
              fileToUpload.name,
              currentMediaType,
              false
            );
          }

          setMediaName(defaultName);
        }
      }
    }, [fileToUpload, contextObjectName, hardcodeType, isArticle, articleName]);

    useEffect(() => {
      if (mediaFilename && mediaType > 0) {
        let defaultName = "";

        if (isArticle && articleName && contextObjectName) {
          defaultName = generateDefaultMediaName(
            contextObjectName,
            mediaFilename,
            articleName,
            true
          );
        } else if (contextObjectName && contextObjectName.trim() !== "") {
          const currentMediaType = hardcodeType
            ? MEDIA_TYPE_VALUES[hardcodeType]
            : mediaType;
          defaultName = generateDefaultMediaName(
            contextObjectName,
            mediaFilename,
            currentMediaType,
            false
          );
        } else {
          const currentMediaType = hardcodeType
            ? MEDIA_TYPE_VALUES[hardcodeType]
            : mediaType;
          defaultName = generateDefaultMediaName(
            "",
            mediaFilename,
            currentMediaType,
            false
          );
        }

        setMediaName(defaultName);
      }
    }, [
      mediaType,
      contextObjectName,
      mediaFilename,
      hardcodeType,
      isArticle,
      articleName,
    ]);

    useEffect(() => {
      if (mediaFile) {
        if (
          previousMediaUrlRef.current &&
          previousMediaUrlRef.current.startsWith("blob:")
        ) {
          clearBlobAndGLTFCache(previousMediaUrlRef.current);
        }

        const newBlobUrl = URL.createObjectURL(mediaFile as Blob);
        setMediaUrl(newBlobUrl);
        previousMediaUrlRef.current = newBlobUrl;
        setIsPreviewLoaded(false);
      }
    }, [mediaFile]);

    const handleSave = async () => {
      if (!mediaFile) return;

      setIsLoading(true);
      setError(null);

      const uploadStartTime = Date.now();

      try {
        const effectiveMediaType = hardcodeType
          ? (MEDIA_TYPE_VALUES[hardcodeType] as number)
          : mediaType;

        const media = await uploadMedia(
          mediaFilename,
          effectiveMediaType,
          mediaFile,
          mediaName
        );
        if (media) {
          if (afterUploadSight) {
            await afterUploadSight(media.id);
          } else if (afterUpload) {
            await afterUpload(media);
          }
        }

        if (effectiveMediaType === 2) {
          const uploadDurationSec = Math.round((Date.now() - uploadStartTime) / 1000);
          const minutes = Math.floor(uploadDurationSec / 60);
          const seconds = uploadDurationSec % 60;
          const durationStr = minutes > 0
            ? `${minutes} мин ${seconds} сек`
            : `${seconds} сек`;

          const fileSizeMb = mediaFile.size / (1024 * 1024);
          const fileSizeStr = fileSizeMb >= 1024
            ? `${(fileSizeMb / 1024).toFixed(2)} ГБ`
            : `${fileSizeMb.toFixed(1)} МБ`;

          if (authStore.canRead("snapshots")) {
            try {
              await snapshotStore.getStorageInfo();
              const storage = snapshotStore.storageInfo;
              if (storage) {
                toast.success(
                  `Видео (${fileSizeStr}) загружено за ${durationStr}. Свободно на диске: ${storage.available_disk_space_gb.toFixed(2)} ГБ из ${storage.total_disk_space_gb.toFixed(2)} ГБ`,
                  { autoClose: 8000 }
                );
              } else {
                toast.success(`Видео (${fileSizeStr}) загружено за ${durationStr}`, { autoClose: 6000 });
              }
            } catch {
              toast.success(`Видео (${fileSizeStr}) загружено за ${durationStr}`, { autoClose: 6000 });
            }
          } else {
            toast.success(`Видео (${fileSizeStr}) загружено за ${durationStr}`, { autoClose: 6000 });
          }
        }

        setSuccess(true);

        setTimeout(() => {
          handleClose();
        }, 1000);
      } catch (err) {
        setError(err instanceof Error ? err.message : "Failed to save media");
      } finally {
        setIsLoading(false);
      }
    };

    const handleClose = () => {
      if (
        previousMediaUrlRef.current &&
        previousMediaUrlRef.current.startsWith("blob:")
      ) {
        clearBlobAndGLTFCache(previousMediaUrlRef.current);
      }

      setError(null);
      setSuccess(false);
      setMediaUrl(null);
      setMediaFile(null);
      setIsPreviewLoaded(false);
      previousMediaUrlRef.current = null;
      onClose();
    };

    return (
      <>
        <Dialog open={open} onClose={handleClose} maxWidth="md" fullWidth>
          <DialogTitle>Просмотр медиа</DialogTitle>
          <DialogContent
            className="flex gap-4"
            dividers
            sx={{
              display: "flex",
              flexDirection: "column",
              gap: 2,
              pt: 2,
            }}
          >
            <Box className="flex flex-col gap-4">
              <Box className="flex gap-2">
                <TextField
                  fullWidth
                  value={mediaName}
                  onChange={(e) => setMediaName(e.target.value)}
                  label="Название медиа"
                  disabled={isLoading}
                />
                <TextField
                  fullWidth
                  value={mediaFilename}
                  onChange={(e) => setMediaFilename(e.target.value)}
                  label="Название файла"
                  disabled={isLoading}
                />
              </Box>

              <FormControl fullWidth sx={{ width: "50%" }}>
                <InputLabel>Тип медиа</InputLabel>
                <Select
                  value={
                    hardcodeType ? MEDIA_TYPE_VALUES[hardcodeType] : mediaType
                  }
                  disabled={!!hardcodeType}
                  label="Тип медиа"
                  onChange={(e) => setMediaType(Number(e.target.value))}
                >
                  {availableMediaTypes.map((type) => (
                    <MenuItem key={type} value={type}>
                      {
                        MEDIA_TYPE_LABELS[
                          type as keyof typeof MEDIA_TYPE_LABELS
                        ]
                      }
                    </MenuItem>
                  ))}
                </Select>
              </FormControl>

              <Box className="flex gap-4 h-[40vh]">
                <Paper
                  elevation={2}
                  sx={{
                    flex: 1,
                    p: 2,
                    display: "flex",
                    alignItems: "center",
                    justifyContent: "center",
                    height: "100%",
                    position: "relative",
                  }}
                >
                  {!isPreviewLoaded && mediaUrl && (
                    <Box
                      sx={{
                        position: "absolute",
                        top: "50%",
                        left: "50%",
                        transform: "translate(-50%, -50%)",
                        zIndex: 1,
                      }}
                    >
                      <CircularProgress />
                    </Box>
                  )}
                  {mediaType == 2 && mediaUrl && (
                    <video
                      src={mediaUrl}
                      autoPlay
                      muted
                      loop
                      controls
                      style={{ maxWidth: "100%", maxHeight: "100%" }}
                      onLoadedData={() => setIsPreviewLoaded(true)}
                      onError={() => setIsPreviewLoaded(true)}
                    />
                  )}
                  {mediaType === 6 && mediaUrl && (
                    <ModelViewer3D
                      fileUrl={mediaUrl}
                      height="100%"
                      onLoad={() => setIsPreviewLoaded(true)}
                    />
                  )}
                  {mediaType !== 6 && mediaType !== 2 && mediaUrl && (
                    <img
                      src={mediaUrl ?? ""}
                      alt="Uploaded media"
                      style={{
                        height: "100%",
                        objectFit: "contain",
                      }}
                      onLoad={() => setIsPreviewLoaded(true)}
                      onError={() => setIsPreviewLoaded(true)}
                    />
                  )}
                </Paper>

                <Box className="flex flex-col gap-2 self-end">
                  <Button
                    variant="contained"
                    sx={{
                      backgroundColor: isLoading ? "#9e9e9e" : "#4caf50",
                      "&:hover": {
                        backgroundColor: isLoading ? "#9e9e9e" : "#45a049",
                      },
                    }}
                    startIcon={
                      isLoading ? (
                        <CircularProgress size={16} color="inherit" />
                      ) : (
                        <Save size={16} />
                      )
                    }
                    onClick={handleSave}
                    disabled={
                      isLoading ||
                      (!mediaName && !mediaFilename) ||
                      !isPreviewLoaded
                    }
                  >
                    {isLoading
                      ? "Сохранение..."
                      : !isPreviewLoaded
                      ? "Загрузка превью..."
                      : "Сохранить"}
                  </Button>
                </Box>
              </Box>
            </Box>
          </DialogContent>
          <DialogActions>
            <Button onClick={handleClose} disabled={isLoading}>
              Отмена
            </Button>
          </DialogActions>
        </Dialog>

        <Snackbar
          open={!!error}
          autoHideDuration={6000}
          onClose={() => setError(null)}
        >
          <Alert severity="error" onClose={() => setError(null)}>
            {error}
          </Alert>
        </Snackbar>

        <Snackbar
          open={success}
          autoHideDuration={3000}
          onClose={() => setSuccess(false)}
        >
          <Alert severity="success" onClose={() => setSuccess(false)}>
            Медиа успешно сохранено
          </Alert>
        </Snackbar>
      </>
    );
  }
);
