<template>
  <section
    v-if="notificationsSupported"
    id="sectionDailyNotifications"
    class="bg-slate-100 rounded-md overflow-hidden px-4 py-4 mt-8 mb-8"
    aria-labelledby="dailyNotificationsHeading"
  >
    <h2 id="dailyNotificationsHeading" class="mb-2 font-bold">
      Daily Notifications
      <button
        class="text-slate-400 fa-fw cursor-pointer"
        aria-label="Learn more about native notifications"
        @click.stop="showNativeNotificationInfo"
      >
        <font-awesome icon="circle-question" aria-hidden="true" />
      </button>
    </h2>

    <div class="flex items-center justify-between">
      <div>Daily Notification</div>
      <!-- Toggle switch -->
      <div
        class="relative ml-2 cursor-pointer"
        role="switch"
        :aria-checked="nativeNotificationEnabled"
        :aria-label="
          nativeNotificationEnabled
            ? 'Disable daily notifications'
            : 'Enable daily notifications'
        "
        tabindex="0"
        @click="toggleNativeNotification"
      >
        <!-- input -->
        <input
          :checked="nativeNotificationEnabled"
          type="checkbox"
          class="sr-only"
          tabindex="-1"
          readonly
        />
        <!-- line -->
        <div
          class="block bg-slate-500 w-14 h-8 rounded-full transition"
          :class="{
            'bg-blue-600': nativeNotificationEnabled,
          }"
        ></div>
        <!-- dot -->
        <div
          class="dot absolute left-1 top-1 bg-slate-400 w-6 h-6 rounded-full transition"
          :class="{
            'left-7 bg-white': nativeNotificationEnabled,
          }"
        ></div>
      </div>
    </div>

    <!-- Show "Open Settings" button when permissions are denied -->
    <div
      v-if="
        notificationsSupported &&
        notificationStatus &&
        notificationStatus.permissions.notifications === 'denied'
      "
      class="mt-2"
    >
      <button
        class="w-full px-3 py-2 bg-blue-600 text-white rounded text-sm font-medium"
        @click="openNotificationSettings"
      >
        Open Settings
      </button>
      <p class="text-xs text-slate-500 mt-1 text-center">
        Enable notifications in Settings > App info > Notifications
      </p>
    </div>

    <!-- Time input section - show when enabled OR when no time is set -->
    <div
      v-if="nativeNotificationEnabled || !nativeNotificationTimeStorage"
      class="mt-2"
    >
      <div
        v-if="nativeNotificationEnabled"
        class="flex items-center justify-between mb-2"
      >
        <span
          >Scheduled for:
          <span v-if="nativeNotificationTime">{{
            nativeNotificationTime
          }}</span>
          <span v-else class="text-slate-500">Not set</span></span
        >
        <button
          class="text-blue-500 text-sm"
          @click="editNativeNotificationTime"
        >
          {{ showTimeEdit ? "Cancel" : "Edit Time" }}
        </button>
      </div>

      <!-- Time input (shown when editing or when no time is set) -->
      <div v-if="showTimeEdit || !nativeNotificationTimeStorage" class="mt-2">
        <label class="block text-sm text-slate-600 mb-1">
          Notification Time
        </label>
        <div class="flex items-center gap-2">
          <input
            v-model="nativeNotificationTimeStorage"
            type="time"
            class="rounded border border-slate-400 px-2 py-2"
            @change="onTimeChange"
          />
          <button
            v-if="showTimeEdit || nativeNotificationTimeStorage"
            class="px-3 py-2 bg-blue-600 text-white rounded"
            @click="saveTimeChange"
          >
            Save
          </button>
        </div>
        <p
          v-if="!nativeNotificationTimeStorage"
          class="text-xs text-slate-500 mt-1"
        >
          Set a time before enabling notifications
        </p>
      </div>
    </div>

    <!-- Loading state -->
    <div v-if="loading" class="mt-2 text-sm text-slate-500">Loading...</div>
  </section>
</template>

<script lang="ts">
/**
 * DailyNotificationSection Component
 *
 * A self-contained component for managing daily notification scheduling
 * in AccountViewView. This component handles platform detection, permission
 * requests, scheduling, and state management for daily notifications.
 *
 * Features:
 * - Platform capability detection (hides on unsupported platforms)
 * - Permission request flow
 * - Schedule/cancel notifications
 * - Time editing with HTML5 time input
 * - Settings persistence
 * - Plugin state synchronization
 *
 * @author Generated for TimeSafari Daily Notification Integration
 * @component
 */

import { Component, Vue } from "vue-facing-decorator";
import { PlatformServiceMixin } from "@/utils/PlatformServiceMixin";
import { logger } from "@/utils/logger";
import { PlatformServiceFactory } from "@/services/PlatformServiceFactory";
import type {
  NotificationStatus,
  PermissionStatus,
} from "@/services/PlatformService";
import { createNotifyHelpers, TIMEOUTS } from "@/utils/notify";
import type { NotificationIface } from "@/constants/app";

/**
 * Convert 24-hour time format ("09:00") to 12-hour display format ("9:00 AM")
 */
function formatTimeForDisplay(time24: string): string {
  if (!time24) return "";
  const [hours, minutes] = time24.split(":");
  const hourNum = parseInt(hours);
  const isPM = hourNum >= 12;
  const displayHour =
    hourNum === 0 ? 12 : hourNum > 12 ? hourNum - 12 : hourNum;
  return `${displayHour}:${minutes} ${isPM ? "PM" : "AM"}`;
}

@Component({
  name: "DailyNotificationSection",
  mixins: [PlatformServiceMixin],
})
export default class DailyNotificationSection extends Vue {
  $notify!: (notification: NotificationIface, timeout?: number) => void;

  // Component state
  notificationsSupported: boolean = false;
  nativeNotificationEnabled: boolean = false;
  nativeNotificationTime: string = ""; // Display format: "9:00 AM"
  nativeNotificationTimeStorage: string = ""; // Plugin format: "09:00"
  nativeNotificationTitle: string = "Daily Update";
  nativeNotificationMessage: string = "Your daily notification is ready!";
  showTimeEdit: boolean = false;
  loading: boolean = false;
  notificationStatus: NotificationStatus | null = null;

  // Notify helpers
  private notify!: ReturnType<typeof createNotifyHelpers>;

  async created(): Promise<void> {
    this.notify = createNotifyHelpers(this.$notify);
  }

  /**
   * Initialize component state on mount
   * Checks platform support and syncs with plugin state
   *
   * **Token Refresh on Mount:**
   * - Refreshes native fetcher configuration to ensure plugin has valid token
   * - This handles cases where app was closed for extended periods
   * - Token refresh happens automatically without user interaction
   *
   * **App Resume Listener:**
   * - Listens for Capacitor 'resume' event to refresh token when app comes to foreground
   * - Ensures plugin always has fresh token for background prefetch operations
   * - Cleaned up in `beforeDestroy()` lifecycle hook
   */
  async mounted(): Promise<void> {
    await this.initializeState();
    // Refresh native fetcher configuration on mount
    // This ensures plugin has valid token even if app was closed for extended periods
    await this.refreshNativeFetcherConfig();
    // Listen for app resume events to refresh token when app comes to foreground
    // This is part of the proactive token refresh strategy
    document.addEventListener("resume", this.handleAppResume);
  }

  /**
   * Cleanup on component destroy
   */
  beforeDestroy(): void {
    document.removeEventListener("resume", this.handleAppResume);
  }

  /**
   * Handle app resume event - refresh native fetcher configuration
   *
   * This method is called when the app comes to foreground (via Capacitor 'resume' event).
   * It proactively refreshes the JWT token to ensure the plugin has valid authentication
   * for background prefetch operations.
   *
   * **Why refresh on resume?**
   * - Tokens expire after 72 hours
   * - App may have been closed for extended periods
   * - Refreshing ensures plugin has valid token for next prefetch cycle
   * - No user interaction required - happens automatically
   *
   * @see {@link refreshNativeFetcherConfig} For implementation details
   */
  async handleAppResume(): Promise<void> {
    logger.debug(
      "[DailyNotificationSection] App resumed, refreshing native fetcher config",
    );
    await this.refreshNativeFetcherConfig();
  }

  /**
   * Refresh native fetcher configuration with fresh JWT token
   *
   * This method ensures the daily notification plugin has a valid authentication token
   * for background prefetch operations. It's called proactively to prevent token expiration
   * issues during offline periods.
   *
   * **Refresh Triggers:**
   * - Component mount (when notification settings page loads)
   * - App resume (when app comes to foreground)
   * - Notification enabled (when user enables daily notifications)
   *
   * **Token Refresh Strategy (Hybrid Approach):**
   * - Tokens are valid for 72 hours (see `accessTokenForBackground`)
   * - Tokens are refreshed proactively when app is already open
   * - If token expires while offline, plugin uses cached content
   * - Next time app opens, token is automatically refreshed
   *
   * **Why This Approach?**
   * - No app wake-up required (tokens refresh when app is already open)
   * - Works offline (72-hour validity supports extended offline periods)
   * - Automatic (no user interaction required)
   * - Includes starred plans (fetcher receives user's starred plans for prefetch)
   * - Graceful degradation (if refresh fails, cached content still works)
   *
   * **Error Handling:**
   * - Errors are logged but not shown to user (background operation)
   * - Returns early if notifications not supported or disabled
   * - Returns early if API server not configured
   * - Failures don't interrupt user experience
   *
   * @returns Promise that resolves when refresh completes (or fails silently)
   *
   * @example
   * ```typescript
   * // Called automatically on mount/resume
   * await this.refreshNativeFetcherConfig();
   * ```
   *
   * @see {@link CapacitorPlatformService.configureNativeFetcher} For token generation
   * @see {@link accessTokenForBackground} For 72-hour token generation
   */
  async refreshNativeFetcherConfig(): Promise<void> {
    try {
      const platformService = PlatformServiceFactory.getInstance();

      // Early return: Only refresh if notifications are supported and enabled
      // This prevents unnecessary work when notifications aren't being used
      if (!this.notificationsSupported || !this.nativeNotificationEnabled) {
        return;
      }

      // Get settings for API server and starred plans
      // API server tells plugin where to fetch content from
      // Starred plans tell plugin which plans to prefetch
      const settings = await this.$accountSettings();
      const apiServer = settings.apiServer || "";

      if (!apiServer) {
        logger.warn(
          "[DailyNotificationSection] No API server configured, skipping native fetcher refresh",
        );
        return;
      }

      // Get starred plans from settings
      // These are passed to the plugin so it knows which plans to prefetch
      const starredPlanHandleIds = settings.starredPlanHandleIds || [];

      // Configure native fetcher with fresh token
      // The jwt parameter is ignored - configureNativeFetcher generates it automatically
      // This ensures we always have a fresh token with current expiration time
      await platformService.configureNativeFetcher({
        apiServer,
        jwt: "", // Will be generated automatically by configureNativeFetcher
        starredPlanHandleIds,
      });

      logger.info(
        "[DailyNotificationSection] Native fetcher configuration refreshed",
      );
    } catch (error) {
      // Don't show error to user - this is a background operation
      // Failures are logged for debugging but don't interrupt user experience
      // If refresh fails, plugin will use existing token (if still valid) or cached content
      logger.error(
        "[DailyNotificationSection] Failed to refresh native fetcher config:",
        error,
      );
    }
  }

  /**
   * Initialize component state
   * Checks platform support and syncs with plugin state
   */
  async initializeState(): Promise<void> {
    try {
      this.loading = true;
      const platformService = PlatformServiceFactory.getInstance();

      // Check if notifications are supported on this platform
      // This also verifies plugin availability (returns null if plugin unavailable)
      const status = await platformService.getDailyNotificationStatus();
      if (status === null) {
        // Notifications not supported or plugin unavailable - don't initialize
        this.notificationsSupported = false;
        logger.warn(
          "[DailyNotificationSection] Notifications not supported or plugin unavailable",
        );
        return;
      }

      this.notificationsSupported = true;
      this.notificationStatus = status;

      // Plugin state is the source of truth
      if (status.isScheduled && status.scheduledTime) {
        // Plugin has a scheduled notification - sync UI to match
        this.nativeNotificationEnabled = true;
        this.nativeNotificationTimeStorage = status.scheduledTime;
        this.nativeNotificationTime = formatTimeForDisplay(
          status.scheduledTime,
        );
      } else {
        // No plugin schedule - UI defaults to disabled
        this.nativeNotificationEnabled = false;
        this.nativeNotificationTimeStorage = "";
        this.nativeNotificationTime = "";
      }
    } catch (error) {
      logger.error("[DailyNotificationSection] Failed to initialize:", error);
      this.notificationsSupported = false;
    } finally {
      this.loading = false;
    }
  }

  /**
   * Toggle notification on/off
   */
  async toggleNativeNotification(): Promise<void> {
    // Prevent multiple simultaneous toggles
    if (this.loading) {
      logger.warn(
        "[DailyNotificationSection] Toggle ignored - operation in progress",
      );
      return;
    }

    logger.info(
      `[DailyNotificationSection] Toggling notification: ${this.nativeNotificationEnabled} -> ${!this.nativeNotificationEnabled}`,
    );

    if (this.nativeNotificationEnabled) {
      await this.disableNativeNotification();
    } else {
      await this.enableNativeNotification();
    }
  }

  /**
   * Enable daily notification
   */
  async enableNativeNotification(): Promise<void> {
    try {
      this.loading = true;
      const platformService = PlatformServiceFactory.getInstance();

      // Check if we have a time set
      if (!this.nativeNotificationTimeStorage) {
        this.notify.error(
          "Please set a notification time first",
          TIMEOUTS.SHORT,
        );
        this.loading = false;
        return;
      }

      // Check permissions first - this also verifies plugin availability
      let permissions: PermissionStatus | null;
      try {
        permissions = await platformService.checkNotificationPermissions();
        logger.info(
          `[DailyNotificationSection] Permission check result:`,
          permissions,
        );
      } catch (error) {
        // Plugin may not be available or there's an error
        logger.error(
          "[DailyNotificationSection] Failed to check permissions (plugin may be unavailable):",
          error,
        );
        this.notify.error(
          "Unable to check notification permissions. The notification plugin may not be installed.",
          TIMEOUTS.LONG,
        );
        this.nativeNotificationEnabled = false;
        return;
      }

      if (permissions === null) {
        // Platform doesn't support notifications or plugin unavailable
        logger.warn(
          "[DailyNotificationSection] Notifications not supported or plugin unavailable",
        );
        this.notify.error(
          "Notifications are not supported on this platform or the plugin is not installed.",
          TIMEOUTS.SHORT,
        );
        this.nativeNotificationEnabled = false;
        return;
      }

      logger.info(
        `[DailyNotificationSection] Permission state: ${permissions.notifications}`,
      );

      // If permissions are explicitly denied, don't try to request again
      // (this prevents the plugin crash when handling denied permissions)
      // Android won't show the dialog again if permissions are permanently denied
      if (permissions.notifications === "denied") {
        logger.warn(
          "[DailyNotificationSection] Permissions already denied, directing user to settings",
        );
        this.notify.error(
          "Notification permissions were denied. Tap 'Open Settings' to enable them.",
          TIMEOUTS.LONG,
        );
        this.nativeNotificationEnabled = false;
        return;
      }

      // Only request if permissions are in "prompt" state (not denied, not granted)
      // This ensures we only call requestPermissions when Android will actually show a dialog
      if (permissions.notifications === "prompt") {
        logger.info(
          "[DailyNotificationSection] Permission state is 'prompt', requesting permissions...",
        );
        try {
          const result = await platformService.requestNotificationPermissions();
          logger.info(
            `[DailyNotificationSection] Permission request result:`,
            result,
          );
          if (result === null) {
            // Plugin unavailable or request failed
            logger.error(
              "[DailyNotificationSection] Permission request returned null",
            );
            this.notify.error(
              "Unable to request notification permissions. The plugin may not be available.",
              TIMEOUTS.LONG,
            );
            this.nativeNotificationEnabled = false;
            return;
          }
          if (!result.notifications) {
            // Permission request was denied
            logger.warn(
              "[DailyNotificationSection] Permission request denied by user",
            );
            this.notify.error(
              "Notification permissions are required. Tap 'Open Settings' to enable them.",
              TIMEOUTS.LONG,
            );
            this.nativeNotificationEnabled = false;
            return;
          }
          // Permissions granted - continue
          logger.info(
            "[DailyNotificationSection] Permissions granted successfully",
          );
        } catch (error) {
          // Handle permission request errors (including plugin crashes)
          logger.error(
            "[DailyNotificationSection] Permission request failed:",
            error,
          );
          this.notify.error(
            "Unable to request notification permissions. Tap 'Open Settings' to enable them.",
            TIMEOUTS.LONG,
          );
          this.nativeNotificationEnabled = false;
          return;
        }
      } else if (permissions.notifications !== "granted") {
        // Unexpected state - shouldn't happen, but handle gracefully
        logger.warn(
          `[DailyNotificationSection] Unexpected permission state: ${permissions.notifications}`,
        );
        this.notify.error(
          "Unable to determine notification permission status. Tap 'Open Settings' to check.",
          TIMEOUTS.LONG,
        );
        this.nativeNotificationEnabled = false;
        return;
      } else {
        logger.info("[DailyNotificationSection] Permissions already granted");
      }

      // Permissions are granted - continue with scheduling

      // Schedule notification via PlatformService
      await platformService.scheduleDailyNotification({
        time: this.nativeNotificationTimeStorage, // "09:00" in local time
        title: this.nativeNotificationTitle,
        body: this.nativeNotificationMessage,
        sound: true,
        priority: "high",
      });

      // Update UI state
      this.nativeNotificationEnabled = true;

      // Refresh native fetcher configuration with fresh token
      // This ensures plugin has valid authentication when notifications are first enabled
      // Token will be valid for 72 hours, supporting offline prefetch operations
      await this.refreshNativeFetcherConfig();

      this.notify.success(
        "Daily notification scheduled successfully",
        TIMEOUTS.SHORT,
      );
    } catch (error) {
      logger.error(
        "[DailyNotificationSection] Failed to enable notification:",
        error,
      );
      this.notify.error(
        "Failed to schedule notification. Please try again.",
        TIMEOUTS.LONG,
      );
      this.nativeNotificationEnabled = false;
    } finally {
      this.loading = false;
    }
  }

  /**
   * Disable daily notification
   */
  async disableNativeNotification(): Promise<void> {
    try {
      this.loading = true;
      const platformService = PlatformServiceFactory.getInstance();

      // Cancel notification via PlatformService
      await platformService.cancelDailyNotification();

      // Update UI state
      this.nativeNotificationEnabled = false;
      this.nativeNotificationTime = "";
      this.nativeNotificationTimeStorage = "";
      this.showTimeEdit = false;

      this.notify.success("Daily notification disabled", TIMEOUTS.SHORT);
    } catch (error) {
      logger.error(
        "[DailyNotificationSection] Failed to disable notification:",
        error,
      );
      this.notify.error(
        "Failed to disable notification. Please try again.",
        TIMEOUTS.LONG,
      );
    } finally {
      this.loading = false;
    }
  }

  /**
   * Show/hide time edit input
   */
  editNativeNotificationTime(): void {
    this.showTimeEdit = !this.showTimeEdit;
  }

  /**
   * Handle time input change
   */
  onTimeChange(): void {
    // Time is already in nativeNotificationTimeStorage via v-model
    // Just update display format
    if (this.nativeNotificationTimeStorage) {
      this.nativeNotificationTime = formatTimeForDisplay(
        this.nativeNotificationTimeStorage,
      );
    }
  }

  /**
   * Save time change and update notification schedule
   */
  async saveTimeChange(): Promise<void> {
    if (!this.nativeNotificationTimeStorage) {
      this.notify.error("Please select a time", TIMEOUTS.SHORT);
      return;
    }

    // Update display format
    this.nativeNotificationTime = formatTimeForDisplay(
      this.nativeNotificationTimeStorage,
    );

    // If notification is enabled, update the schedule
    if (this.nativeNotificationEnabled) {
      await this.updateNotificationTime(this.nativeNotificationTimeStorage);
    } else {
      // Just update local state (time preference stored in component)
      this.showTimeEdit = false;
      this.notify.success("Notification time saved", TIMEOUTS.SHORT);
    }
  }

  /**
   * Update notification time
   * If notification is enabled, immediately updates the schedule
   */
  async updateNotificationTime(newTime: string): Promise<void> {
    // newTime is in "HH:mm" format from HTML5 time input
    if (!this.nativeNotificationEnabled) {
      // If notification is disabled, just update local state
      this.nativeNotificationTimeStorage = newTime;
      this.nativeNotificationTime = formatTimeForDisplay(newTime);
      this.showTimeEdit = false;
      return;
    }

    // Notification is enabled - update the schedule
    try {
      this.loading = true;
      const platformService = PlatformServiceFactory.getInstance();

      // 1. Cancel existing notification
      await platformService.cancelDailyNotification();

      // 2. Schedule with new time
      await platformService.scheduleDailyNotification({
        time: newTime, // "09:00" in local time
        title: this.nativeNotificationTitle,
        body: this.nativeNotificationMessage,
        sound: true,
        priority: "high",
      });

      // 3. Update local state
      this.nativeNotificationTimeStorage = newTime;
      this.nativeNotificationTime = formatTimeForDisplay(newTime);

      this.notify.success(
        "Notification time updated successfully",
        TIMEOUTS.SHORT,
      );
      this.showTimeEdit = false;
    } catch (error) {
      logger.error(
        "[DailyNotificationSection] Failed to update notification time:",
        error,
      );
      this.notify.error(
        "Failed to update notification time. Please try again.",
        TIMEOUTS.LONG,
      );
    } finally {
      this.loading = false;
    }
  }

  /**
   * Show info dialog about native notifications
   */
  showNativeNotificationInfo(): void {
    // TODO: Implement info dialog or navigate to help page
    this.notify.info(
      "Daily notifications use your device's native notification system. They work even when the app is closed.",
      TIMEOUTS.STANDARD,
    );
  }

  /**
   * Open app notification settings
   */
  async openNotificationSettings(): Promise<void> {
    try {
      const platformService = PlatformServiceFactory.getInstance();
      const result = await platformService.openAppNotificationSettings();
      if (result === null) {
        this.notify.error(
          "Unable to open settings. Please go to Settings > Apps > TimeSafari > Notifications manually.",
          TIMEOUTS.LONG,
        );
      } else {
        this.notify.success("Opening notification settings...", TIMEOUTS.SHORT);
      }
    } catch (error) {
      logger.error(
        "[DailyNotificationSection] Failed to open notification settings:",
        error,
      );
      this.notify.error(
        "Unable to open settings. Please go to Settings > Apps > TimeSafari > Notifications manually.",
        TIMEOUTS.LONG,
      );
    }
  }
}
</script>

<style scoped>
.dot {
  transition: left 0.2s ease;
}
</style>
