/**
 * ProfileService - Handles user profile operations and API calls
 * Extracted from AccountViewView.vue to improve separation of concerns
 */

import { AxiosInstance, AxiosError } from "axios";
import { UserProfile } from "@/libs/partnerServer";
import { UserProfileResponse } from "@/interfaces/accountView";
import { getHeaders, errorStringForLog } from "@/libs/endorserServer";
import { handleApiError } from "./api";
import { logger } from "@/utils/logger";
import { ACCOUNT_VIEW_CONSTANTS } from "@/constants/accountView";

/**
 * Profile data interface
 */
export interface ProfileData {
  description: string;
  latitude: number;
  longitude: number;
  includeLocation: boolean;
}

/**
 * Profile service class
 */
export class ProfileService {
  private axios: AxiosInstance;
  private partnerApiServer: string;

  constructor(axios: AxiosInstance, partnerApiServer: string) {
    this.axios = axios;
    this.partnerApiServer = partnerApiServer;
  }

  /**
   * Load user profile from the server
   * @param activeDid - The user's DID
   * @returns ProfileData or null if profile doesn't exist
   */
  async loadProfile(activeDid: string): Promise<ProfileData | null> {
    try {
      const headers = await getHeaders(activeDid);
      const response = await this.axios.get<UserProfileResponse>(
        `${this.partnerApiServer}/api/partner/userProfileForIssuer/${activeDid}`,
        { headers },
      );

      if (response.status === 200) {
        const data = response.data.data;
        const profileData: ProfileData = {
          description: data.description || "",
          latitude: data.locLat || 0,
          longitude: data.locLon || 0,
          includeLocation: !!(data.locLat && data.locLon),
        };
        return profileData;
      } else {
        throw new Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.UNABLE_TO_LOAD_PROFILE);
      }
    } catch (error) {
      if (this.isApiError(error) && error.response?.status === 404) {
        // Profile doesn't exist yet - this is normal
        return null;
      }

      logger.error("Error loading profile:", errorStringForLog(error));
      handleApiError(error as AxiosError, "/api/partner/userProfileForIssuer");
      return null;
    }
  }

  /**
   * Save user profile to the server
   * @param activeDid - The user's DID
   * @param profileData - The profile data to save
   * @returns true if successful, false otherwise
   */
  async saveProfile(
    activeDid: string,
    profileData: ProfileData,
  ): Promise<boolean> {
    try {
      const headers = await getHeaders(activeDid);
      const payload: UserProfile = {
        description: profileData.description,
        issuerDid: activeDid,
      };

      // Add location data if location is included
      if (
        profileData.includeLocation &&
        profileData.latitude &&
        profileData.longitude
      ) {
        payload.locLat = profileData.latitude;
        payload.locLon = profileData.longitude;
      }

      const response = await this.axios.post(
        `${this.partnerApiServer}/api/partner/userProfile`,
        payload,
        { headers },
      );

      if (response.status === 201) {
        return true;
      } else {
        logger.error("Error saving profile:", response);
        throw new Error(ACCOUNT_VIEW_CONSTANTS.ERRORS.PROFILE_NOT_SAVED);
      }
    } catch (error) {
      logger.error("Error saving profile:", errorStringForLog(error));
      handleApiError(error as AxiosError, "/api/partner/userProfile");
      return false;
    }
  }

  /**
   * Delete user profile from the server
   * @param activeDid - The user's DID
   * @returns true if successful, false otherwise
   */
  async deleteProfile(activeDid: string): Promise<boolean> {
    try {
      const headers = await getHeaders(activeDid);
      const url = `${this.partnerApiServer}/api/partner/userProfile`;
      const response = await this.axios.delete(url, { headers });

      if (response.status === 204 || response.status === 200) {
        logger.info("Profile deleted successfully");
        return true;
      } else {
        logger.error("Unexpected response status when deleting profile:", {
          status: response.status,
          statusText: response.statusText,
          data: response.data,
        });
        throw new Error(
          `Profile not deleted - HTTP ${response.status}: ${response.statusText}`,
        );
      }
    } catch (error) {
      if (this.isApiError(error) && error.response) {
        const response = error.response;
        logger.error("API error deleting profile:", {
          status: response.status,
          statusText: response.statusText,
          data: response.data,
          url: this.getErrorUrl(error),
        });

        // Handle specific HTTP status codes
        if (response.status === 204) {
          logger.debug("Profile deleted successfully (204 No Content)");
          return true; // 204 is success for DELETE operations
        } else if (response.status === 404) {
          logger.warn("Profile not found - may already be deleted");
          return true; // Consider this a success if profile doesn't exist
        } else if (response.status === 400) {
          logger.error("Bad request when deleting profile:", response.data);
          const errorMessage =
            typeof response.data === "string"
              ? response.data
              : response.data?.message || "Bad request";
          throw new Error(`Profile deletion failed: ${errorMessage}`);
        } else if (response.status === 401) {
          logger.error("Unauthorized to delete profile");
          throw new Error("You are not authorized to delete this profile");
        } else if (response.status === 403) {
          logger.error("Forbidden to delete profile");
          throw new Error("You are not allowed to delete this profile");
        }
      }

      logger.error("Error deleting profile:", errorStringForLog(error));
      handleApiError(error as AxiosError, "/api/partner/userProfile");
      return false;
    }
  }

  /**
   * Update profile location
   * @param profileData - Current profile data
   * @param latitude - New latitude
   * @param longitude - New longitude
   * @returns Updated profile data
   */
  updateProfileLocation(
    profileData: ProfileData,
    latitude: number,
    longitude: number,
  ): ProfileData {
    return {
      ...profileData,
      latitude,
      longitude,
      includeLocation: true,
    };
  }

  /**
   * Toggle location inclusion in profile
   * @param profileData - Current profile data
   * @returns Updated profile data
   */
  toggleProfileLocation(profileData: ProfileData): ProfileData {
    const includeLocation = !profileData.includeLocation;
    return {
      ...profileData,
      latitude: includeLocation ? profileData.latitude : 0,
      longitude: includeLocation ? profileData.longitude : 0,
      includeLocation,
    };
  }

  /**
   * Clear profile location
   * @param profileData - Current profile data
   * @returns Updated profile data
   */
  clearProfileLocation(profileData: ProfileData): ProfileData {
    return {
      ...profileData,
      latitude: 0,
      longitude: 0,
      includeLocation: false,
    };
  }

  /**
   * Reset profile to default state
   * @returns Default profile data
   */
  getDefaultProfile(): ProfileData {
    return {
      description: "",
      latitude: 0,
      longitude: 0,
      includeLocation: false,
    };
  }

  /**
   * Type guard for API errors with proper typing
   */
  private isApiError(error: unknown): error is {
    response?: {
      status?: number;
      statusText?: string;
      data?: { message?: string } | string;
    };
  } {
    return typeof error === "object" && error !== null && "response" in error;
  }

  /**
   * Extract error URL safely from error object
   */
  private getErrorUrl(error: unknown): string | undefined {
    if (this.isAxiosError(error)) {
      return error.config?.url;
    }
    if (this.isApiError(error) && this.hasConfigProperty(error)) {
      const config = this.getConfigProperty(error);
      return config?.url;
    }
    return undefined;
  }

  /**
   * Type guard to check if error has config property
   */
  private hasConfigProperty(
    error: unknown,
  ): error is { config?: { url?: string } } {
    return typeof error === "object" && error !== null && "config" in error;
  }

  /**
   * Safely extract config property from error
   */
  private getConfigProperty(error: {
    config?: { url?: string };
  }): { url?: string } | undefined {
    return error.config;
  }

  /**
   * Type guard for AxiosError
   */
  private isAxiosError(error: unknown): error is AxiosError {
    return error instanceof AxiosError;
  }
}

/**
 * Factory function to create a ProfileService instance
 */
export function createProfileService(
  axios: AxiosInstance,
  partnerApiServer: string,
): ProfileService {
  return new ProfileService(axios, partnerApiServer);
}
