import { router, Stack } from "expo-router";
import { useCallback, useEffect, useState } from "react";
import {
  ActivityIndicator,
  Alert,
  FlatList,
  Text,
  TextInput,
  TouchableOpacity,
  View,
} from "react-native";
import ProtectedScreen from "./components/ProtectedScreen";
import { useAuth } from "./lib/nhost/AuthProvider";
import { commonStyles } from "./styles/commonStyles";
import { colors } from "./styles/theme";

// The interfaces below define the structure of our data
// They are not strictly necessary but help with type safety

// Represents a single todo item
interface Todo {
  id: string;
  title: string;
  details: string | null;
  completed: boolean;
  created_at: string;
  updated_at: string;
  user_id: string;
}

// This matches the GraphQL response structure for fetching todos
// Can be used as a generic type on the request method
interface GetTodos {
  todos: Todo[];
}

// This matches the GraphQL response structure for inserting a todo
// Can be used as a generic type on the request method
interface InsertTodo {
  insert_todos_one: Todo | null;
}

// This matches the GraphQL response structure for updating a todo
// Can be used as a generic type on the request method
interface UpdateTodo {
  update_todos_by_pk: Todo | null;
}

export default function Todos() {
  const { nhost, session } = useAuth();
  const [todos, setTodos] = useState<Todo[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [newTodoTitle, setNewTodoTitle] = useState("");
  const [newTodoDetails, setNewTodoDetails] = useState("");
  const [editingTodo, setEditingTodo] = useState<Todo | null>(null);
  const [showAddForm, setShowAddForm] = useState(false);
  const [expandedTodos, setExpandedTodos] = useState<Set<string>>(new Set());
  const [addingTodo, setAddingTodo] = useState(false);
  const [updatingTodos, setUpdatingTodos] = useState<Set<string>>(new Set());

  // Redirect to sign in if not authenticated
  useEffect(() => {
    if (!session) {
      router.replace("/signin");
    }
  }, [session]);

  const fetchTodos = useCallback(async () => {
    try {
      setLoading(true);
      // Make GraphQL request to fetch todos using Nhost client
      // The query automatically filters by user_id due to Hasura permissions
      const response = await nhost.graphql.request<GetTodos>({
        query: `
          query GetTodos {
            todos(order_by: { created_at: desc }) {
              id
              title
              details
              completed
              created_at
              updated_at
              user_id
            }
          }
        `,
      });

      // Check for GraphQL errors in the response body
      if (response.body.errors) {
        throw new Error(
          response.body.errors[0]?.message || "Failed to fetch todos",
        );
      }

      // Extract todos from the GraphQL response data
      setTodos(response.body?.data?.todos || []);
      setError(null);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to fetch todos");
    } finally {
      setLoading(false);
    }
  }, [nhost.graphql]);

  const addTodo = async () => {
    if (!newTodoTitle.trim()) return;

    try {
      setAddingTodo(true);
      // Execute GraphQL mutation to insert a new todo
      // user_id is automatically set by Hasura based on JWT token
      const response = await nhost.graphql.request<InsertTodo>({
        query: `
          mutation InsertTodo($title: String!, $details: String) {
            insert_todos_one(object: { title: $title, details: $details }) {
              id
              title
              details
              completed
              created_at
              updated_at
              user_id
            }
          }
        `,
        variables: {
          title: newTodoTitle.trim(),
          details: newTodoDetails.trim() || null,
        },
      });

      if (response.body.errors) {
        throw new Error(
          response.body.errors[0]?.message || "Failed to add todo",
        );
      }

      if (!response.body?.data?.insert_todos_one) {
        throw new Error("Failed to add todo");
      }
      setTodos([response.body?.data?.insert_todos_one, ...todos]);
      setNewTodoTitle("");
      setNewTodoDetails("");
      setShowAddForm(false);
      setError(null);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to add todo");
      Alert.alert(
        "Error",
        err instanceof Error ? err.message : "Failed to add todo",
      );
    } finally {
      setAddingTodo(false);
    }
  };

  const updateTodo = async (
    id: string,
    updates: Partial<Pick<Todo, "title" | "details" | "completed">>,
  ) => {
    try {
      setUpdatingTodos((prev) => new Set([...prev, id]));
      // Execute GraphQL mutation to update an existing todo by primary key
      // Hasura permissions ensure users can only update their own todos
      const response = await nhost.graphql.request<UpdateTodo>({
        query: `
          mutation UpdateTodo($id: uuid!, $updates: todos_set_input!) {
            update_todos_by_pk(pk_columns: { id: $id }, _set: $updates) {
              id
              title
              details
              completed
              created_at
              updated_at
              user_id
            }
          }
        `,
        variables: {
          id,
          updates,
        },
      });

      if (response.body.errors) {
        throw new Error(
          response.body.errors[0]?.message || "Failed to update todo",
        );
      }

      if (!response.body?.data?.update_todos_by_pk) {
        throw new Error("Failed to update todo");
      }

      const updatedTodo = response.body?.data?.update_todos_by_pk;
      if (updatedTodo) {
        setTodos(todos.map((todo) => (todo.id === id ? updatedTodo : todo)));
      }
      setEditingTodo(null);
      setError(null);
    } catch (err) {
      setError(err instanceof Error ? err.message : "Failed to update todo");
      Alert.alert(
        "Error",
        err instanceof Error ? err.message : "Failed to update todo",
      );
    } finally {
      setUpdatingTodos((prev) => {
        const newSet = new Set(prev);
        newSet.delete(id);
        return newSet;
      });
    }
  };

  const deleteTodo = async (id: string) => {
    Alert.alert("Delete Todo", "Are you sure you want to delete this todo?", [
      { text: "Cancel", style: "cancel" },
      {
        text: "Delete",
        style: "destructive",
        onPress: async () => {
          try {
            setUpdatingTodos((prev) => new Set([...prev, id]));
            // Execute GraphQL mutation to delete a todo by primary key
            // Hasura permissions ensure users can only delete their own todos
            const response = await nhost.graphql.request({
              query: `
                  mutation DeleteTodo($id: uuid!) {
                    delete_todos_by_pk(id: $id) {
                      id
                    }
                  }
                `,
              variables: {
                id,
              },
            });

            if (response.body.errors) {
              throw new Error(
                response.body.errors[0]?.message || "Failed to delete todo",
              );
            }

            setTodos(todos.filter((todo) => todo.id !== id));
            setError(null);
          } catch (err) {
            setError(
              err instanceof Error ? err.message : "Failed to delete todo",
            );
            Alert.alert(
              "Error",
              err instanceof Error ? err.message : "Failed to delete todo",
            );
          } finally {
            setUpdatingTodos((prev) => {
              const newSet = new Set(prev);
              newSet.delete(id);
              return newSet;
            });
          }
        },
      },
    ]);
  };

  const toggleComplete = async (todo: Todo) => {
    await updateTodo(todo.id, { completed: !todo.completed });
  };

  const saveEdit = async () => {
    if (!editingTodo) return;
    await updateTodo(editingTodo.id, {
      title: editingTodo.title,
      details: editingTodo.details,
    });
  };

  const toggleTodoExpansion = (todoId: string) => {
    const newExpanded = new Set(expandedTodos);
    if (newExpanded.has(todoId)) {
      newExpanded.delete(todoId);
    } else {
      newExpanded.add(todoId);
    }
    setExpandedTodos(newExpanded);
  };

  // Fetch todos when user session is available
  // The session contains the JWT token needed for GraphQL authentication
  useEffect(() => {
    if (session) {
      fetchTodos();
    }
  }, [session, fetchTodos]);

  if (!session) {
    return null; // Will redirect to sign in
  }

  const renderTodoItem = ({ item: todo }: { item: Todo }) => {
    const isUpdating = updatingTodos.has(todo.id);
    const isExpanded = expandedTodos.has(todo.id);

    return (
      <View
        style={[
          commonStyles.todoCard,
          todo.completed && commonStyles.todoCompleted,
        ]}
      >
        {editingTodo?.id === todo.id ? (
          <View style={commonStyles.todoEditForm}>
            <Text style={commonStyles.inputLabel}>Title</Text>
            <TextInput
              style={commonStyles.input}
              value={editingTodo.title}
              onChangeText={(text) =>
                setEditingTodo({
                  ...editingTodo,
                  title: text,
                })
              }
              placeholder="Enter todo title"
              placeholderTextColor={colors.textPlaceholder}
            />
            <Text style={commonStyles.inputLabel}>Details</Text>
            <TextInput
              style={[commonStyles.input, commonStyles.textArea]}
              value={editingTodo.details || ""}
              onChangeText={(text) =>
                setEditingTodo({
                  ...editingTodo,
                  details: text,
                })
              }
              placeholder="Enter details (optional)"
              placeholderTextColor={colors.textPlaceholder}
              multiline
              numberOfLines={3}
            />
            <View style={commonStyles.buttonGroup}>
              <TouchableOpacity
                style={[commonStyles.button, commonStyles.primaryButton]}
                onPress={saveEdit}
                disabled={isUpdating}
              >
                <Text style={commonStyles.buttonText}>
                  {isUpdating ? "Saving..." : "Save"}
                </Text>
              </TouchableOpacity>
              <TouchableOpacity
                style={[commonStyles.button, commonStyles.secondaryButton]}
                onPress={() => setEditingTodo(null)}
              >
                <Text
                  style={[
                    commonStyles.buttonText,
                    commonStyles.secondaryButtonText,
                  ]}
                >
                  Cancel
                </Text>
              </TouchableOpacity>
            </View>
          </View>
        ) : (
          <View>
            <View style={commonStyles.todoHeader}>
              <TouchableOpacity
                style={commonStyles.todoTitleContainer}
                onPress={() => toggleTodoExpansion(todo.id)}
              >
                <Text
                  style={[
                    commonStyles.todoTitle,
                    todo.completed && commonStyles.todoTitleCompleted,
                  ]}
                >
                  {todo.title}
                </Text>
              </TouchableOpacity>
              <View style={commonStyles.todoActions}>
                <TouchableOpacity
                  style={[
                    commonStyles.actionButton,
                    commonStyles.completeButton,
                  ]}
                  onPress={() => toggleComplete(todo)}
                  disabled={isUpdating}
                >
                  <Text style={commonStyles.actionButtonText}>
                    {isUpdating ? "⌛" : todo.completed ? "↶" : "✓"}
                  </Text>
                </TouchableOpacity>
                <TouchableOpacity
                  style={[commonStyles.actionButton, commonStyles.editButton]}
                  onPress={() => setEditingTodo(todo)}
                >
                  <Text style={commonStyles.actionButtonText}>✏️</Text>
                </TouchableOpacity>
                <TouchableOpacity
                  style={[commonStyles.actionButton, commonStyles.deleteButton]}
                  onPress={() => deleteTodo(todo.id)}
                  disabled={isUpdating}
                >
                  <Text style={commonStyles.actionButtonText}>🗑️</Text>
                </TouchableOpacity>
              </View>
            </View>
            {isExpanded && (
              <View style={commonStyles.todoDetails}>
                {todo.details && (
                  <Text
                    style={[
                      commonStyles.todoDescription,
                      todo.completed && commonStyles.todoDescriptionCompleted,
                    ]}
                  >
                    {todo.details}
                  </Text>
                )}
                <View style={commonStyles.todoMeta}>
                  <Text style={commonStyles.metaText}>
                    Created: {new Date(todo.created_at).toLocaleString()}
                  </Text>
                  <Text style={commonStyles.metaText}>
                    Updated: {new Date(todo.updated_at).toLocaleString()}
                  </Text>
                  {todo.completed && (
                    <View style={commonStyles.completionBadge}>
                      <Text style={commonStyles.completionText}>
                        ✅ Completed
                      </Text>
                    </View>
                  )}
                </View>
              </View>
            )}
          </View>
        )}
      </View>
    );
  };

  const renderHeader = () => (
    <>
      <View style={commonStyles.pageHeader}>
        <Text style={commonStyles.pageTitle}>My Todos</Text>
        {!showAddForm && (
          <TouchableOpacity
            style={commonStyles.addButton}
            onPress={() => setShowAddForm(true)}
          >
            <Text style={commonStyles.addButtonText}>+</Text>
          </TouchableOpacity>
        )}
      </View>

      {error && (
        <View style={[commonStyles.errorContainer, { marginHorizontal: 16 }]}>
          <Text style={commonStyles.errorText}>Error: {error}</Text>
        </View>
      )}

      {showAddForm && (
        <View
          style={[
            commonStyles.card,
            { marginHorizontal: 16, width: undefined },
          ]}
        >
          <Text style={commonStyles.cardTitle}>Add New Todo</Text>
          <View style={commonStyles.formFields}>
            <View style={commonStyles.fieldGroup}>
              <Text style={commonStyles.inputLabel}>Title *</Text>
              <TextInput
                style={commonStyles.input}
                value={newTodoTitle}
                onChangeText={setNewTodoTitle}
                placeholder="What needs to be done?"
                placeholderTextColor={colors.textPlaceholder}
              />
            </View>
            <View style={commonStyles.fieldGroup}>
              <Text style={commonStyles.inputLabel}>Details</Text>
              <TextInput
                style={[commonStyles.input, commonStyles.textArea]}
                value={newTodoDetails}
                onChangeText={setNewTodoDetails}
                placeholder="Add some details (optional)..."
                placeholderTextColor={colors.textPlaceholder}
                multiline
                numberOfLines={3}
              />
            </View>
            <View style={commonStyles.buttonGroup}>
              <TouchableOpacity
                style={[commonStyles.button, commonStyles.primaryButton]}
                onPress={addTodo}
                disabled={addingTodo || !newTodoTitle.trim()}
              >
                <Text style={commonStyles.buttonText}>
                  {addingTodo ? "Adding..." : "Add Todo"}
                </Text>
              </TouchableOpacity>
              <TouchableOpacity
                style={[commonStyles.button, commonStyles.secondaryButton]}
                onPress={() => {
                  setShowAddForm(false);
                  setNewTodoTitle("");
                  setNewTodoDetails("");
                }}
              >
                <Text
                  style={[
                    commonStyles.buttonText,
                    commonStyles.secondaryButtonText,
                  ]}
                >
                  Cancel
                </Text>
              </TouchableOpacity>
            </View>
          </View>
        </View>
      )}
    </>
  );

  const renderEmptyState = () => (
    <View style={commonStyles.emptyState}>
      <Text style={commonStyles.emptyStateTitle}>No todos yet</Text>
      <Text style={commonStyles.emptyStateText}>
        Create your first todo to get started!
      </Text>
    </View>
  );

  if (loading) {
    return (
      <ProtectedScreen>
        <Stack.Screen options={{ title: "My Todos" }} />
        <View style={commonStyles.loadingContainer}>
          <ActivityIndicator size="large" color="#6366f1" />
          <Text style={commonStyles.loadingText}>Loading todos...</Text>
        </View>
      </ProtectedScreen>
    );
  }

  return (
    <ProtectedScreen>
      <Stack.Screen options={{ title: "My Todos" }} />
      <View style={commonStyles.container}>
        {renderHeader()}
        <FlatList
          data={showAddForm ? [] : todos}
          renderItem={renderTodoItem}
          keyExtractor={(item) => item.id}
          ListEmptyComponent={!showAddForm ? renderEmptyState : null}
          showsVerticalScrollIndicator={false}
          contentContainerStyle={commonStyles.listContainer}
          keyboardShouldPersistTaps="handled"
        />
      </View>
    </ProtectedScreen>
  );
}
