import type { FetchError } from "@nhost/nhost-js/fetch";
import type { ErrorResponse, FileMetadata } from "@nhost/nhost-js/storage";
import * as DocumentPicker from "expo-document-picker";
import * as FileSystem from "expo-file-system";
import { Stack } from "expo-router";
import * as Sharing from "expo-sharing";
import { useCallback, useEffect, useState } from "react";
import {
  ActivityIndicator,
  Alert,
  FlatList,
  Text,
  TouchableOpacity,
  View,
} from "react-native";
import ProtectedScreen from "./components/ProtectedScreen";
import { useAuth } from "./lib/nhost/AuthProvider";
import { commonStyles, fileUploadStyles } from "./styles/commonStyles";
import { colors } from "./styles/theme";
interface DeleteStatus {
  message: string;
  isError: boolean;
}
interface GraphqlGetFilesResponse {
  files: FileMetadata[];
}
// Utility function to format file size
function formatFileSize(bytes: number, decimals = 2): string {
  if (bytes === 0) return "0 Bytes";
  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return `${parseFloat((bytes / k ** i).toFixed(dm))} ${sizes[i]}`;
}
// Convert Blob to Base64 for React Native file handling
function blobToBase64(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onloadend = () => {
      const base64data = reader.result as string;
      // Remove the data URL prefix (e.g., "data:application/octet-stream;base64,")
      const base64Content = base64data.split(",")[1] || "";
      resolve(base64Content);
    };
    reader.onerror = reject;
    reader.readAsDataURL(blob);
  });
}
export default function Files() {
  const { nhost } = useAuth();
  const [selectedFile, setSelectedFile] =
    useState<DocumentPicker.DocumentPickerResult | null>(null);
  const [uploading, setUploading] = useState<boolean>(false);
  const [uploadResult, setUploadResult] = useState<FileMetadata | null>(null);
  const [isFetching, setIsFetching] = useState<boolean>(true);
  const [error, setError] = useState<string | null>(null);
  const [files, setFiles] = useState<FileMetadata[]>([]);
  const [viewingFile, setViewingFile] = useState<string | null>(null);
  const [deleting, setDeleting] = useState<string | null>(null);
  const [deleteStatus, setDeleteStatus] = useState<DeleteStatus | null>(null);
  const [isViewingInProgress, setIsViewingInProgress] =
    useState<boolean>(false);
  const fetchFiles = useCallback(async () => {
    setIsFetching(true);
    setError(null);
    try {
      // Fetch files using GraphQL query
      const response = await nhost.graphql.request<GraphqlGetFilesResponse>({
        query: `query GetFiles {
          files {
            id
            name
            size
            mimeType
            bucketId
            uploadedByUserId
          }
        }`,
      });
      setFiles(response.body.data?.files || []);
    } catch (err) {
      const errMessage =
        err instanceof Error ? err.message : "An unexpected error occurred";
      setError(`Failed to fetch files: ${errMessage}`);
    } finally {
      setIsFetching(false);
    }
  }, [nhost.graphql]);
  // Fetch existing files when component mounts
  useEffect(() => {
    void fetchFiles();
  }, [fetchFiles]);
  const pickDocument = async () => {
    // Prevent DocumentPicker from opening if we're currently viewing a file
    if (isViewingInProgress) {
      return;
    }
    try {
      const result = await DocumentPicker.getDocumentAsync({
        type: "*/*", // All file types
        copyToCacheDirectory: true,
      });
      if (!result.canceled) {
        setSelectedFile(result);
        setError(null);
        setUploadResult(null);
      }
    } catch (err) {
      setError("Failed to pick document");
      console.error("DocumentPicker Error:", err);
    }
  };
  const handleUpload = async () => {
    if (!selectedFile || selectedFile.canceled) {
      setError("Please select a file to upload");
      return;
    }
    setUploading(true);
    setError(null);
    try {
      // For React Native, we need to read the file first
      const fileToUpload = selectedFile.assets?.[0];
      if (!fileToUpload) {
        throw new Error("No file selected");
      }
      const file: unknown = {
        uri: fileToUpload.uri,
        name: fileToUpload.name || "file",
        type: fileToUpload.mimeType || "application/octet-stream",
      };
      // Upload file to the personal bucket
      // The uploadedByUserId is automatically set by the storage permissions
      const response = await nhost.storage.uploadFiles({
        "bucket-id": "personal",
        "file[]": [file as File],
      });
      // Get the processed file data
      const uploadedFile = response.body.processedFiles?.[0];
      if (uploadedFile === undefined) {
        throw new Error("Failed to upload file");
      }
      setUploadResult(uploadedFile);
      // Reset form
      setSelectedFile(null);
      // Update files list
      setFiles((prevFiles) => [uploadedFile, ...prevFiles]);
      // Refresh file list
      await fetchFiles();
      // Clear success message after 3 seconds
      setTimeout(() => {
        setUploadResult(null);
      }, 3000);
    } catch (err: unknown) {
      const error = err as FetchError<ErrorResponse>;
      setError(`Failed to upload file: ${error.message}`);
      console.error("Upload error:", err);
    } finally {
      setUploading(false);
    }
  };
  // Function to handle viewing a file with proper authorization
  const handleViewFile = async (
    fileId: string,
    fileName: string,
    mimeType: string,
  ) => {
    setViewingFile(fileId);
    setIsViewingInProgress(true);
    try {
      // Fetch the file with authentication using the SDK
      const response = await nhost.storage.getFile(fileId);
      if (!response.body) {
        throw new Error("Failed to retrieve file contents");
      }
      // For iOS/Android, we need to save the file to the device first
      // Create a unique temp file path with a timestamp to prevent collisions
      const timestamp = Date.now();
      const tempFileName = fileName.includes(".")
        ? fileName
        : `${fileName}.file`;
      const tempFilePath = `${FileSystem.cacheDirectory}${timestamp}_${tempFileName}`;
      // Get the blob from the response
      const blob = response.body;
      // Convert blob to base64
      const base64Data = await blobToBase64(blob);
      // Write the file to the filesystem
      await FileSystem.writeAsStringAsync(tempFilePath, base64Data, {
        encoding: FileSystem.EncodingType.Base64,
      });
      // Check if sharing is available (iOS & Android)
      const isSharingAvailable = await Sharing.isAvailableAsync();
      if (isSharingAvailable) {
        // Open the file with the default app
        await Sharing.shareAsync(tempFilePath, {
          mimeType: mimeType || "application/octet-stream",
          dialogTitle: `View ${fileName}`,
          UTI: mimeType, // for iOS
        });
        // Clean up the temp file after sharing
        try {
          await FileSystem.deleteAsync(tempFilePath, { idempotent: true });
        } catch (cleanupErr) {
          console.warn("Failed to clean up temp file:", cleanupErr);
        }
        // Add a delay before allowing new document picker actions
        // This prevents iOS from triggering file selection dialogs
        setTimeout(() => {
          setIsViewingInProgress(false);
        }, 1000);
      } else {
        throw new Error("Sharing is not available on this device");
      }
    } catch (err) {
      const error = err as FetchError<ErrorResponse>;
      setError(`Failed to view file: ${error.message}`);
      console.error("Error viewing file:", err);
      Alert.alert("Error", `Failed to view file: ${error.message}`);
      setIsViewingInProgress(false);
    } finally {
      setViewingFile(null);
    }
  };
  // Function to handle deleting a file
  const handleDeleteFile = (fileId: string) => {
    if (!fileId || deleting) return;
    // Confirm deletion
    Alert.alert("Delete File", "Are you sure you want to delete this file?", [
      {
        text: "Cancel",
        style: "cancel",
      },
      {
        text: "Delete",
        style: "destructive",
        onPress: () => {
          void (async () => {
            setDeleting(fileId);
            setError(null);
            setDeleteStatus(null);
            // Get the file name for the status message
            const fileToDelete = files.find((file) => file.id === fileId);
            const fileName = fileToDelete?.name || "File";
            try {
              // Delete the file using the Nhost storage SDK
              // Permissions ensure users can only delete their own files
              await nhost.storage.deleteFile(fileId);
              // Show success message
              setDeleteStatus({
                message: `${fileName} deleted successfully`,
                isError: false,
              });
              // Update the local files list by removing the deleted file
              setFiles(files.filter((file) => file.id !== fileId));
              // Refresh the file list
              await fetchFiles();
              // Clear the success message after 3 seconds
              setTimeout(() => {
                setDeleteStatus(null);
              }, 3000);
            } catch (err) {
              // Show error message
              const error = err as FetchError<ErrorResponse>;
              setDeleteStatus({
                message: `Failed to delete ${fileName}: ${error.message}`,
                isError: true,
              });
              console.error("Error deleting file:", err);
            } finally {
              setDeleting(null);
            }
          })();
        },
      },
    ]);
  };
  return (
    <ProtectedScreen>
      <Stack.Screen options={{ title: "File Upload" }} />
      <View style={commonStyles.container}>
        {/* Upload Form */}
        <View style={commonStyles.card}>
          <Text style={commonStyles.cardTitle}>Upload a File</Text>
          <TouchableOpacity
            style={fileUploadStyles.fileUpload}
            onPress={pickDocument}
          >
            <View style={fileUploadStyles.uploadIcon}>
              <Text style={fileUploadStyles.uploadIconText}>⬆️</Text>
            </View>
            <Text style={fileUploadStyles.uploadText}>
              Tap to select a file
            </Text>
            {selectedFile &&
              !selectedFile.canceled &&
              selectedFile.assets?.[0] && (
                <Text style={fileUploadStyles.fileName}>
                  {selectedFile.assets[0].name} (
                  {formatFileSize(selectedFile.assets[0].size || 0)})
                </Text>
              )}
          </TouchableOpacity>
          {error && (
            <View style={commonStyles.errorContainer}>
              <Text style={commonStyles.errorText}>{error}</Text>
            </View>
          )}
          {uploadResult && (
            <View style={commonStyles.successContainer}>
              <Text style={commonStyles.successText}>
                File uploaded successfully!
              </Text>
            </View>
          )}
          <TouchableOpacity
            style={[
              commonStyles.button,
              (!selectedFile || selectedFile.canceled || uploading) &&
                fileUploadStyles.buttonDisabled,
            ]}
            onPress={handleUpload}
            disabled={!selectedFile || selectedFile.canceled || uploading}
          >
            <Text style={commonStyles.buttonText}>
              {uploading ? "Uploading..." : "Upload File"}
            </Text>
          </TouchableOpacity>
        </View>
        {/* Files List */}
        <View style={commonStyles.card}>
          <Text style={commonStyles.cardTitle}>Your Files</Text>
          {deleteStatus && (
            <View
              style={[
                deleteStatus.isError
                  ? commonStyles.errorContainer
                  : commonStyles.successContainer,
              ]}
            >
              <Text
                style={
                  deleteStatus.isError
                    ? commonStyles.errorText
                    : commonStyles.successText
                }
              >
                {deleteStatus.message}
              </Text>
            </View>
          )}
          {isFetching ? (
            <View style={commonStyles.loadingContainer}>
              <ActivityIndicator size="large" color={colors.primary} />
              <Text style={commonStyles.loadingText}>Loading files...</Text>
            </View>
          ) : files.length === 0 ? (
            <View style={fileUploadStyles.emptyState}>
              <Text style={fileUploadStyles.emptyIcon}>📄</Text>
              <Text style={fileUploadStyles.emptyTitle}>No files yet</Text>
              <Text style={fileUploadStyles.emptyDescription}>
                Upload your first file to get started!
              </Text>
            </View>
          ) : (
            <FlatList
              data={files}
              keyExtractor={(item) => item.id || Math.random().toString()}
              renderItem={({ item }) => (
                <View style={fileUploadStyles.fileItem}>
                  <View style={fileUploadStyles.fileInfo}>
                    <Text
                      style={fileUploadStyles.fileNameText}
                      numberOfLines={1}
                    >
                      {item.name}
                    </Text>
                    <Text style={fileUploadStyles.fileDetails}>
                      {item.mimeType} • {formatFileSize(item.size || 0)}
                    </Text>
                  </View>
                  <View style={fileUploadStyles.fileActions}>
                    <TouchableOpacity
                      style={fileUploadStyles.actionButton}
                      onPress={() =>
                        handleViewFile(
                          item.id || "unknown",
                          item.name || "unknown",
                          item.mimeType || "unknown",
                        )
                      }
                      disabled={viewingFile === item.id}
                    >
                      {viewingFile === item.id ? (
                        <Text style={fileUploadStyles.actionText}>⌛</Text>
                      ) : (
                        <Text style={fileUploadStyles.actionText}>👁️</Text>
                      )}
                    </TouchableOpacity>
                    <TouchableOpacity
                      style={[
                        fileUploadStyles.actionButton,
                        fileUploadStyles.deleteButton,
                      ]}
                      onPress={() => handleDeleteFile(item.id || "unknown")}
                      disabled={deleting === item.id}
                    >
                      {deleting === item.id ? (
                        <Text style={fileUploadStyles.actionText}>⌛</Text>
                      ) : (
                        <Text style={fileUploadStyles.actionText}>🗑️</Text>
                      )}
                    </TouchableOpacity>
                  </View>
                </View>
              )}
              style={fileUploadStyles.fileList}
            />
          )}
        </View>
      </View>
    </ProtectedScreen>
  );
}