import {AntDesign, Ionicons} from "@expo/vector-icons";
import React, {useCallback, useEffect, useState} from "react";
import {Button, Checkbox, Text, View} from "react-native-ui-lib";
import {ActivityIndicator, ScrollView, StyleSheet} from "react-native";
import {TouchableOpacity} from "react-native-gesture-handler";
import {useAuthContext} from "@/contexts/AuthContext";
import {useUpdateUserData} from "@/hooks/firebase/useUpdateUserData";
import debounce from "debounce";
import AppleIcon from "@/assets/svgs/AppleIcon";
import GoogleIcon from "@/assets/svgs/GoogleIcon";
import OutlookIcon from "@/assets/svgs/OutlookIcon";
import * as AuthSession from "expo-auth-session";
import * as Google from "expo-auth-session/providers/google";
import * as WebBrowser from "expo-web-browser";
import {UserProfile} from "@firebase/auth";
import {useFetchAndSaveGoogleEvents} from "@/hooks/useFetchAndSaveGoogleEvents";
import {useFetchAndSaveOutlookEvents} from "@/hooks/useFetchAndSaveOutlookEvents";
import {useFetchAndSaveAppleEvents} from "@/hooks/useFetchAndSaveAppleEvents";
import * as AppleAuthentication from 'expo-apple-authentication';
import ExpoLocalization from "expo-localization/src/ExpoLocalization";
import {colorMap} from "@/constants/colorMap";

const googleConfig = {
    androidClientId:
        "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
    iosClientId:
        "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
    webClientId:
        "406146460310-2u67ab2nbhu23trp8auho1fq4om29fc0.apps.googleusercontent.com",
    scopes: [
        "email",
        "profile",
        "https://www.googleapis.com/auth/calendar.events.owned",
    ],
};

const microsoftConfig = {
    clientId: "13c79071-1066-40a9-9f71-b8c4b138b4af",
    redirectUri: AuthSession.makeRedirectUri({path: "settings"}),
    scopes: [
        "openid",
        "profile",
        "email",
        "offline_access",
        "Calendars.ReadWrite",
        "User.Read"
    ],
    authorizationEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize",
    tokenEndpoint: "https://login.microsoftonline.com/common/oauth2/v2.0/token",
};

const CalendarSettingsPage = (props: {
    setSelectedPage: (page: number) => void;
}) => {
    const {profileData} = useAuthContext();
    const [firstDayOfWeek, setFirstDayOfWeek] = useState<string>(profileData?.firstDayOfWeek ?? ExpoLocalization.getCalendars()[0].firstWeekday === 1 ? "Mondays" : "Sundays");

    const [selectedColor, setSelectedColor] = useState<string>(
        profileData?.eventColor ?? colorMap.pink
    );
    const [previousSelectedColor, setPreviousSelectedColor] = useState<string>(
        profileData?.eventColor ?? colorMap.pink
    );

    const {mutateAsync: updateUserData} = useUpdateUserData();
    const {mutateAsync: fetchAndSaveGoogleEvents, isLoading: isSyncingGoogle} = useFetchAndSaveGoogleEvents();
    const {mutateAsync: fetchAndSaveOutlookEvents, isLoading: isSyncingOutlook} = useFetchAndSaveOutlookEvents();
    const {mutateAsync: fetchAndSaveAppleEvents, isLoading: isSyncingApple} = useFetchAndSaveAppleEvents();


    WebBrowser.maybeCompleteAuthSession();
    const [_, response, promptAsync] = Google.useAuthRequest(googleConfig);

    useEffect(() => {
        signInWithGoogle();
    }, [response]);

    const signInWithGoogle = async () => {
        try {
            if (response?.type === "success") {
                const accessToken = response.authentication?.accessToken;

                const userInfoResponse = await fetch(
                    "https://www.googleapis.com/oauth2/v3/userinfo",
                    {
                        headers: {Authorization: `Bearer ${accessToken}`},
                    }
                );

                const userInfo = await userInfoResponse.json();
                const googleMail = userInfo.email;

                let googleAccounts = profileData?.googleAccounts;
                const updatedGoogleAccounts = googleAccounts ? {...googleAccounts, [googleMail]: accessToken} : {[googleMail]: accessToken};

                await updateUserData({
                    newUserData: {googleAccounts: updatedGoogleAccounts},
                });

                await fetchAndSaveGoogleEvents({token: accessToken, email: googleMail})
            }
        } catch (error) {
            console.error("Error during Google sign-in:", error);
        }
    };

    const handleMicrosoftSignIn = async () => {
        try {
            console.log("Starting Microsoft sign-in...");

            const authRequest = new AuthSession.AuthRequest({
                clientId: microsoftConfig.clientId,
                scopes: microsoftConfig.scopes,
                redirectUri: microsoftConfig.redirectUri,
                responseType: AuthSession.ResponseType.Code,
                usePKCE: true, // Enable PKCE
            });

            console.log("Auth request created:", authRequest);

            const authResult = await authRequest.promptAsync({
                authorizationEndpoint: microsoftConfig.authorizationEndpoint,
            });

            console.log("Auth result:", authResult);

            if (authResult.type === "success" && authResult.params?.code) {
                const code = authResult.params.code;
                console.log("Authorization code received:", code);

                // Exchange authorization code for tokens
                const tokenResponse = await fetch(microsoftConfig.tokenEndpoint, {
                    method: "POST",
                    headers: {
                        "Content-Type": "application/x-www-form-urlencoded",
                    },
                    body: `client_id=${
                        microsoftConfig.clientId
                    }&redirect_uri=${encodeURIComponent(
                        microsoftConfig.redirectUri
                    )}&grant_type=authorization_code&code=${code}&code_verifier=${
                        authRequest.codeVerifier
                    }&scope=${encodeURIComponent(
                        "https://graph.microsoft.com/Calendars.ReadWrite offline_access User.Read"
                    )}`,
                });

                console.log("Token response status:", tokenResponse.status);

                if (!tokenResponse.ok) {
                    const errorText = await tokenResponse.text();
                    console.error("Token exchange failed:", errorText);
                    return;
                }

                const tokenData = await tokenResponse.json();
                console.log("Token data received:", tokenData);

                if (tokenData?.access_token) {
                    console.log("Access token received, fetching user info...");

                    // Fetch user info from Microsoft Graph API to get the email
                    const userInfoResponse = await fetch("https://graph.microsoft.com/v1.0/me", {
                        headers: {
                            Authorization: `Bearer ${tokenData.access_token}`,
                        },
                    });

                    const userInfo = await userInfoResponse.json();
                    console.log("User info received:", userInfo);

                    if (userInfo.error) {
                        console.error("Error fetching user info:", userInfo.error);
                    } else {
                        const outlookMail = userInfo.mail || userInfo.userPrincipalName;

                        let microsoftAccounts = profileData?.microsoftAccounts;
                        const updatedMicrosoftAccounts = microsoftAccounts ? {...microsoftAccounts, [outlookMail]: tokenData.access_token} : {[outlookMail]: tokenData.access_token};

                        // Update user data with Microsoft token and email
                        await updateUserData({
                            newUserData: {microsoftAccounts: updatedMicrosoftAccounts},
                        });

                        await fetchAndSaveOutlookEvents(tokenData.access_token, outlookMail)
                        console.log("User data updated successfully.");
                    }
                }
            } else {
                console.warn("Authentication was not successful:", authResult);
            }
        } catch (error) {
            console.error("Error during Microsoft sign-in:", error);
        }
    };

    const handleAppleSignIn = async () => {
        try {
            console.log("Starting Apple Sign-in...");

            const credential = await AppleAuthentication.signInAsync({
                requestedScopes: [
                    AppleAuthentication.AppleAuthenticationScope.EMAIL,
                    AppleAuthentication.AppleAuthenticationScope.FULL_NAME,
                ],
            });

            console.log("Apple sign-in result:", credential);

            const appleToken = credential.identityToken;
            const appleMail = credential.email;

            if (appleToken) {
                console.log("Apple ID token received. Fetch user info if needed...");

                await updateUserData({
                    newUserData: {appleToken, appleMail},
                });

                console.log("User data updated with Apple ID token.");
                await fetchAndSaveAppleEvents({token: appleToken, email: appleMail!});
            } else {
                console.warn("Apple authentication was not successful or email was hidden.");
            }
        } catch (error) {
            console.error("Error during Apple Sign-in:", error);
        }
    };

    const debouncedUpdateUserData = useCallback(
        debounce(async (color: string) => {
            try {
                await updateUserData({
                    newUserData: {
                        eventColor: color,
                    },
                });
            } catch (error) {
                console.error("Failed to update color:", error);
                setSelectedColor(previousSelectedColor);
            }
        }, 500),
        []
    );

    const debouncedUpdateFirstDayOfWeek = useCallback(
        debounce(async (firstDayOfWeek: string) => {
            try {
                await updateUserData({
                    newUserData: {
                        firstDayOfWeek,
                    },
                });
            } catch (error) {
                console.error("Failed to update first day of week:", error);
            }
        }, 500),
        []
    );

    const handleChangeFirstDayOfWeek = (firstDayOfWeek: string) => {
        setFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays");
        debouncedUpdateFirstDayOfWeek(firstDayOfWeek === "Sundays" ? "Mondays" : "Sundays");
    }

    const handleChangeColor = (color: string) => {
        setPreviousSelectedColor(selectedColor);
        setSelectedColor(color);
        debouncedUpdateUserData(color);
    };

    const clearToken = async (provider: "google" | "outlook" | "apple", email: string) => {
        const newUserData: Partial<UserProfile> = {};
        if (provider === "google") {
            let googleAccounts = profileData?.googleAccounts;
            if (googleAccounts) {
                googleAccounts[email] = null;
                newUserData.googleAccounts = googleAccounts;
            }
        } else if (provider === "outlook") {
            let microsoftAccounts = profileData?.microsoftAccounts;
            if (microsoftAccounts) {
                microsoftAccounts[email] = null;
                newUserData.microsoftAccounts = microsoftAccounts;
            }
        } else if (provider === "apple") {
            let appleAccounts = profileData?.appleAccounts;
            if (appleAccounts) {
                appleAccounts[email] = null;
                newUserData.appleAccounts = appleAccounts;
            }
        }
        await updateUserData({newUserData});
    };

    let isConnectedToGoogle = false;
    if (profileData?.googleAccounts) {
        Object.values(profileData?.googleAccounts).forEach((item) => {
            if (item !== null) {
                isConnectedToGoogle = true;
                return;
            }
        });
    }

    let isConnectedToMicrosoft = false;
    const microsoftAccounts  = profileData?.microsoftAccounts;
    if (microsoftAccounts) {
        Object.values(profileData?.microsoftAccounts).forEach((item) => {
            if (item !== null) {
                isConnectedToMicrosoft = true;
                return;
            }
        });
    }

    let isConnectedToApple = false;
    if (profileData?.appleAccounts) {
        Object.values(profileData?.appleAccounts).forEach((item) => {
            if (item !== null) {
                isConnectedToApple = true;
                return;
            }
        });
    }

    return (
        <ScrollView>
            <View marginH-30 marginB-30>
                <TouchableOpacity onPress={() => props.setSelectedPage(0)}>
                    <View row marginT-20 marginB-35 centerV>
                        <Ionicons
                            name="chevron-back"
                            size={14}
                            color="#979797"
                            style={{paddingBottom: 3}}
                        />
                        <Text
                            style={{fontFamily: "Poppins_400Regular", fontSize: 14.71}}
                            color="#979797"
                        >
                            Return to main settings
                        </Text>
                    </View>
                </TouchableOpacity>
                <Text style={styles.subTitle}>Calendar settings</Text>
                <View style={styles.card}>
                    <Text style={styles.cardTitle} marginB-14>
                        Event Color Preference
                    </Text>
                    <View row spread>
                        <TouchableOpacity onPress={() => handleChangeColor(colorMap.pink)}>
                            <View style={styles.colorBox} backgroundColor={colorMap.pink}>
                                {selectedColor == colorMap.pink && (
                                    <AntDesign name="check" size={30} color="white"/>
                                )}
                            </View>
                        </TouchableOpacity>
                        <TouchableOpacity
                            onPress={() => handleChangeColor(colorMap.orange)}
                        >
                            <View style={styles.colorBox} backgroundColor={colorMap.orange}>
                                {selectedColor == colorMap.orange && (
                                    <AntDesign name="check" size={30} color="white"/>
                                )}
                            </View>
                        </TouchableOpacity>
                        <TouchableOpacity onPress={() => handleChangeColor(colorMap.green)}>
                            <View style={styles.colorBox} backgroundColor={colorMap.green}>
                                {selectedColor == colorMap.green && (
                                    <AntDesign name="check" size={30} color="white"/>
                                )}
                            </View>
                        </TouchableOpacity>
                        <TouchableOpacity onPress={() => handleChangeColor(colorMap.teal)}>
                            <View style={styles.colorBox} backgroundColor={colorMap.teal}>
                                {selectedColor == colorMap.teal && (
                                    <AntDesign name="check" size={30} color="white"/>
                                )}
                            </View>
                        </TouchableOpacity>
                        <TouchableOpacity
                            onPress={() => handleChangeColor(colorMap.purple)}
                        >
                            <View style={styles.colorBox} backgroundColor={colorMap.purple}>
                                {selectedColor == colorMap.purple && (
                                    <AntDesign name="check" size={30} color="white"/>
                                )}
                            </View>
                        </TouchableOpacity>
                    </View>
                </View>
                <View style={styles.card}>
                    <Text style={styles.cardTitle}>Weekly Start Date</Text>
                    <View row marginV-5 marginT-20>
                        <Checkbox
                            value={firstDayOfWeek === "Sundays"}
                            style={styles.checkbox}
                            color="#ea156d"
                            onValueChange={() => handleChangeFirstDayOfWeek("Sundays")}
                        />
                        <View row marginL-8>
                            <Text text70>Sundays</Text>
                            <Text text70 color="gray">
                                {" "}
                                (default)
                            </Text>
                        </View>
                    </View>
                    <View row marginV-5>
                        <Checkbox
                            value={firstDayOfWeek === "Mondays"}
                            style={styles.checkbox}
                            color="#ea156d"
                            onValueChange={() => handleChangeFirstDayOfWeek("Mondays")}
                        />
                        <Text text70 marginL-8>
                            Mondays
                        </Text>
                    </View>
                </View>
                <Text style={styles.subTitle} marginT-30 marginB-25>
                    Add Calendar
                </Text>

                <Button
                    onPress={() => promptAsync()}
                    label={"Connect Google"}
                    labelStyle={styles.addCalLbl}
                    labelProps={{
                        numberOfLines: 2
                    }}
                    iconSource={() => (
                        <View marginR-15>
                            <GoogleIcon/>
                        </View>
                    )}
                    style={styles.addCalBtn}
                    color="black"
                    text70BL
                />
                {profileData?.googleAccounts ? Object.keys(profileData?.googleAccounts)?.map((googleMail) => {
                    const googleToken = profileData?.googleAccounts?.[googleMail];
                    return googleToken && <Button
                        key={googleMail}
                        onPress={() => clearToken("google", googleMail)}
                        label={`Disconnect ${googleMail}`}
                        labelStyle={styles.addCalLbl}
                        labelProps={{
                            numberOfLines: 2
                        }}
                        iconSource={() => (
                            <View marginR-15>
                                <GoogleIcon/>
                            </View>
                        )}
                        style={styles.addCalBtn}
                        color="black"
                        text70BL
                    />
                }) : null}

                <Button
                    onPress={() => handleAppleSignIn()}
                    label={"Connect Apple"}
                    labelStyle={styles.addCalLbl}
                    labelProps={{
                        numberOfLines: 2
                    }}
                    iconSource={() => (
                        <View marginR-15>
                            <AppleIcon/>
                        </View>
                    )}
                    style={styles.addCalBtn}
                    color="black"
                    text70BL
                />
                {profileData?.appleAccounts ? Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
                    const appleToken = profileData?.appleAccounts?.[appleEmail];
                    return appleToken && <Button
                        key={appleEmail}
                        onPress={() => clearToken("apple", appleEmail)}
                        label={`Disconnect ${appleEmail}`}
                        labelStyle={styles.addCalLbl}
                        labelProps={{
                            numberOfLines: 2
                        }}
                        iconSource={() => (
                            <View marginR-15>
                                <AppleIcon/>
                            </View>
                        )}
                        style={styles.addCalBtn}
                        color="black"
                        text70BL
                    />
                }) : null}

                <Button
                    onPress={() => handleMicrosoftSignIn()}
                    label={"Connect Outlook"}
                    labelStyle={styles.addCalLbl}
                    labelProps={{
                        numberOfLines: 2
                    }}
                    iconSource={() => (
                        <View marginR-15>
                            <OutlookIcon/>
                        </View>
                    )}
                    style={styles.addCalBtn}
                    color="black"
                    text70BL
                />
                {profileData?.microsoftAccounts ? Object.keys(profileData?.microsoftAccounts)?.map((microsoftEmail) => {
                    const microsoftToken = profileData?.microsoftAccounts?.[microsoftEmail];
                    return microsoftToken && <Button
                        key={microsoftEmail}
                        onPress={() => clearToken("outlook", microsoftEmail)}
                        label={`Disconnect ${microsoftEmail}`}
                        labelStyle={styles.addCalLbl}
                        labelProps={{
                            numberOfLines: 2
                        }}
                        iconSource={() => (
                            <View marginR-15>
                                <OutlookIcon/>
                            </View>
                        )}
                        style={styles.addCalBtn}
                        color="black"
                        text70BL
                    />
                }) : null}

                {(isConnectedToGoogle || isConnectedToMicrosoft || isConnectedToApple) && (
                    <>
                        <Text style={styles.subTitle} marginT-30 marginB-20>
                            Connected Calendars
                        </Text>

                        <View style={styles.noPaddingCard}>
                            <View style={{marginTop: 20}}>
                                {profileData?.googleAccounts && Object.keys(profileData?.googleAccounts)?.map((googleEmail) => {
                                    const googleToken = profileData?.googleAccounts?.[googleEmail];
                                    return googleToken && (
                                        <TouchableOpacity
                                            onPress={() => fetchAndSaveGoogleEvents({token: googleToken, email: googleEmail})}
                                        >
                                            <View row paddingR-20 center>
                                                <Button
                                                    disabled={isSyncingGoogle}
                                                    onPress={() => fetchAndSaveGoogleEvents({token: googleToken, email: googleEmail})}
                                                    label={`Sync ${googleEmail}`}
                                                    labelStyle={styles.addCalLbl}
                                                    labelProps={{numberOfLines: 3}}
                                                    iconSource={() => (
                                                        <View marginR-15>
                                                            <GoogleIcon/>
                                                        </View>
                                                    )}
                                                    style={styles.addCalBtn}
                                                    color="black"
                                                    text70BL
                                                />

                                                {isSyncingGoogle ? (
                                                    <ActivityIndicator/>
                                                ) : (
                                                    <Ionicons name={"refresh"} size={20} color={"#000000"}/>
                                                )}
                                            </View>
                                        </TouchableOpacity>
                                    )
                                })}

                                {profileData?.appleAccounts && Object.keys(profileData?.appleAccounts)?.map((appleEmail) => {
                                    const appleToken = profileData?.appleAccounts?.[appleEmail];
                                    return appleToken && (
                                        <TouchableOpacity
                                            onPress={() => fetchAndSaveAppleEvents({
                                                email: appleEmail,
                                                token: appleToken
                                            })}>
                                            <View row paddingR-20 center>
                                                <Button
                                                    disabled={isSyncingApple}
                                                    onPress={() => fetchAndSaveAppleEvents({
                                                        email: appleEmail,
                                                        token: appleToken
                                                    })}
                                                    label={`Sync ${appleEmail}`}
                                                    labelStyle={styles.addCalLbl}
                                                    labelProps={{numberOfLines: 3}}
                                                    iconSource={() => (
                                                        <View marginR-15>
                                                            <AppleIcon/>
                                                        </View>
                                                    )}
                                                    style={styles.addCalBtn}
                                                    color="black"
                                                    text70BL
                                                />
                                                {isSyncingApple ? (
                                                    <ActivityIndicator/>
                                                ) : (
                                                    <Ionicons name={"refresh"} size={20} color={"#000000"}/>
                                                )}
                                            </View>
                                        </TouchableOpacity>
                                    )
                                })}

                                {profileData?.microsoftAccounts && Object.keys(profileData?.microsoftAccounts)?.map((microsoftEmail) => {
                                    const microsoftToken = profileData?.microsoftAccounts?.[microsoftEmail];
                                    return microsoftToken && (
                                        <TouchableOpacity
                                            onPress={() => fetchAndSaveOutlookEvents({
                                                token: microsoftToken,
                                                email: microsoftEmail
                                            })}
                                        >
                                            <View row paddingR-20 center>
                                                <Button
                                                    disabled={isSyncingOutlook}
                                                    onPress={() => fetchAndSaveOutlookEvents({
                                                        token: microsoftToken,
                                                        email: microsoftEmail
                                                    })}
                                                    label={`Sync ${microsoftEmail}`}
                                                    labelStyle={styles.addCalLbl}
                                                    labelProps={{numberOfLines: 3}}
                                                    iconSource={() => (
                                                        <View marginR-15>
                                                            <OutlookIcon/>
                                                        </View>
                                                    )}
                                                    style={styles.addCalBtn}
                                                    color="black"
                                                    text70BL
                                                />
                                                {isSyncingOutlook ? (
                                                    <ActivityIndicator/>
                                                ) : (
                                                    <Ionicons name={"refresh"} size={20} color={"#000000"}/>
                                                )}
                                            </View>
                                        </TouchableOpacity>
                                    )
                                })}
                            </View>
                        </View>
                    </>
                )}
            </View>
        </ScrollView>
    );
};

const styles = StyleSheet.create({
    addCalBtn: {
        backgroundColor: "#ffffff",
        marginBottom: 15,
        justifyContent: "flex-start",
        paddingLeft: 25,
    },
    backBtn: {
        backgroundColor: "red",
        marginLeft: -2,
        justifyContent: "flex-start",
    },
    card: {
        backgroundColor: "white",
        width: "100%",
        padding: 20,
        paddingBottom: 30,
        marginTop: 20,
        borderRadius: 12,
    },
    noPaddingCard: {
        backgroundColor: "white",
        width: "100%",
        marginTop: 20,
        borderRadius: 12,
    },
    colorBox: {
        aspectRatio: 1,
        justifyContent: "center",
        alignItems: "center",
        width: 51,
        borderRadius: 12,
    },
    checkbox: {
        borderRadius: 50,
    },
    addCalLbl: {
        fontSize: 16,
        fontFamily: "PlusJakartaSan_500Medium",
        flexWrap: "wrap",
        width: "75%",
        textAlign: "left",
        lineHeight: 20,
        overflow: "visible"
    },
    subTitle: {
        fontFamily: "Manrope_600SemiBold",
        fontSize: 18,
    },
    cardTitle: {
        fontFamily: "Manrope_500Medium",
        fontSize: 15,
    },
});

export default CalendarSettingsPage;
