import { supabase } from './utils/supabase';
import {
  InstantLoad,
  UserProfile,
  ANALYSIS_TAB_COOLDOWN_TRUST_MS,
} from './utils/instant-load';
import { setupNavPrefetch } from './utils/prefetch-nav';
import {
  cancelAnalysisLoading,
  completeAnalysisLoading,
  setAnalysisLoadingMessage,
  startAnalysisLoading,
} from './utils/analysis-loading';
import { thumbHashBase64ToDataUrl } from './utils/thumbhash';
import {
  resolveAvatarThumbhashDataUrl,
  setAvatarThumbhashForUser,
} from './utils/avatar-thumbhash-cache';

/** Must match `ANALYSIS_TAB_COOLDOWN_MIN` in track-goals (spacing on /analysis tab). */
const ANALYSIS_TAB_COOLDOWN_MINUTES = 5;

class AnalysisPage {
  private userProfile: UserProfile | null = null;
  private currentUser: any = null;
  private cooldownCache: { timestamp: number; canAnalyze: boolean } | null = null;
  private avatarSignedUrl: string | null = null;
  /** Default landing: "New Analysis" upload; score card only after a run or "Last analysis". */
  private showScoreCard = false;
  private votePreference: boolean | null = null;
  private transientAnalysis: { ai_score: number | null; ai_assessment: string | null; ai_analysis_json: any } | null = null;
  private lastUploadedPreviewUrl: string | null = null;
  private isInitialLoad = true;

  private submissionHistory: {
    id: string;
    created_at: string;
    ai_score: number | null;
    thumbSignedUrl: string | null;
    /** Stored in Supabase; preferred for small list thumbnails (~56px). */
    thumbHashB64: string | null;
  }[] = [];
  private selectedSubmissionId: string | null = null;
  private selectedSubmissionDetail: {
    ai_score: number | null;
    ai_assessment: string | null;
    ai_analysis_json: unknown;
    image_paths: string[];
  } | null = null;
  private selectedSubmissionThumbUrl: string | null = null;

  private elements = {
    loadingState: document.getElementById('loadingState') as HTMLDivElement,
    analysisContent: document.getElementById('analysisContent') as HTMLDivElement,
    errorState: document.getElementById('errorState') as HTMLDivElement,
    retryBtn: document.getElementById('retryBtn') as HTMLButtonElement,

    // Score Card
    scoreCard: document.getElementById('shareable-card') as HTMLElement,
    analysisProCta: document.getElementById('analysisProCta') as HTMLAnchorElement | null,
    newAnalysisBtn: document.getElementById('newAnalysisBtn') as HTMLButtonElement, // New
    scoreAvatar: document.getElementById('scoreAvatar') as HTMLImageElement,
    scoreValue: document.getElementById('score-value') as HTMLElement,
    overallAssessment: document.getElementById('overall-assessment') as HTMLElement,
    potentialNudge: document.getElementById('potential-nudge') as HTMLElement,
    potentialNudgeValue: document.getElementById('potential-nudge-value') as HTMLElement,
    analysisBestFeature: document.getElementById('analysisBestFeature') as HTMLElement | null,
    analysisBestFeatureLocked: document.getElementById('analysisBestFeatureLocked') as HTMLElement | null,
    analysisBestFeatureBody: document.getElementById('analysisBestFeatureBody') as HTMLElement | null,
    analysisBestFeatureName: document.getElementById('analysisBestFeatureName') as HTMLElement | null,
    analysisBestFeatureDesc: document.getElementById('analysisBestFeatureDesc') as HTMLElement | null,
    analysisScoringSummary: document.getElementById('analysisScoringSummary') as HTMLElement | null,
    analysisScoringSummaryLocked: document.getElementById('analysisScoringSummaryLocked') as HTMLElement | null,
    analysisScoringSummaryBody: document.getElementById('analysisScoringSummaryBody') as HTMLElement | null,
    analysisScoringSummaryText: document.getElementById('analysisScoringSummaryText') as HTMLElement | null,
    noScoreOverlay: document.getElementById('noScoreOverlay') as HTMLElement,

    // Upload Section
    uploadSection: document.getElementById('uploadSection') as HTMLElement, // New wrapper
    uploadCard: document.getElementById('uploadCard') as HTMLDivElement,
    lastAnalysisBtn: document.getElementById('lastAnalysisBtn') as HTMLButtonElement,
    getVotedToggle: document.getElementById('getVotedToggle') as HTMLInputElement,
    grChoose: document.getElementById('grChoose') as HTMLButtonElement,
    grFiles: document.getElementById('grFiles') as HTMLInputElement,
    grSubmit: document.getElementById('grSubmit') as HTMLButtonElement,
    grPreview: document.getElementById('grPreview') as HTMLDivElement,
    grStatus: document.getElementById('grStatus') as HTMLDivElement,
    grLocked: document.getElementById('grLocked') as HTMLDivElement,
    profilePicToggleContainer: document.getElementById('profilePicToggleContainer') as HTMLDivElement,
    
    // Upload Notices
    firstAnalysisBadge: document.getElementById('firstAnalysisBadge') as HTMLElement,
    xpBonusBadge: document.getElementById('xpBonusBadge') as HTMLElement,
    uploadNotice: document.getElementById('uploadNotice') as HTMLElement,
    uploadPublicNotice: document.getElementById('uploadPublicNotice') as HTMLElement,
    
    // Cooldown
    uploadCooldownTimer: document.getElementById('uploadCooldownTimer') as HTMLDivElement,
    uploadTimerDisplay: document.getElementById('uploadTimerDisplay') as HTMLDivElement,
    uploadHoursRemaining: document.getElementById('uploadHoursRemaining') as HTMLElement,
    uploadHoursLabel: document.getElementById('uploadHoursLabel') as HTMLElement,
    
    analysisHistorySection: document.getElementById('analysisHistorySection') as HTMLElement | null,
    analysisHistoryList: document.getElementById('analysisHistoryList') as HTMLDivElement | null,
    analysisHistoryEmpty: document.getElementById('analysisHistoryEmpty') as HTMLElement | null
  };

  constructor() {
    this.initializeEventListeners();
    if (this.elements.analysisHistoryList) {
      this.elements.analysisHistoryList.addEventListener('click', (e) => {
        const btn = (e.target as HTMLElement).closest('button[data-submission-id]');
        if (!btn) return;
        const id = btn.getAttribute('data-submission-id');
        if (id) void this.selectSubmission(id);
      });
    }
    this.loadData();

    // Listen for updates from other pages
    InstantLoad.onUpdate((profile) => {
      this.userProfile = profile as any;
      this.avatarSignedUrl = profile.avatar_signed_url || null;
      this.render();
    });
  }

  private initializeEventListeners(): void {
    if (this.elements.retryBtn) {
      this.elements.retryBtn.addEventListener('click', () => this.loadData());
    }

    if (this.elements.newAnalysisBtn) {
        this.elements.newAnalysisBtn.addEventListener('click', () => {
            // Hide card and show upload for a new analysis
            this.showScoreCard = false;
            this.clearSubmissionSelection();
            this.transientAnalysis = null;
            this.toggleSections(false);
            
            window.scrollTo({ top: 0, behavior: 'smooth' });
            
            // Re-check cooldown when opening upload section manually
            this.refreshCooldownFromServer();
        });
    }
    
    if (this.elements.lastAnalysisBtn) {
      this.elements.lastAnalysisBtn.addEventListener('click', () => {
        const hasProfileScore = this.userProfile?.ai_score != null;
        const hasTransient = this.transientAnalysis?.ai_score != null;
        const hasSelected = this.selectedSubmissionDetail?.ai_score != null;
        const hasHistoryScore = this.submissionHistory.some((h) => h.ai_score != null);
        const hasAnyScore =
          hasProfileScore || hasTransient || hasSelected || hasHistoryScore;
        if (!hasAnyScore) return;
        if (this.submissionHistory.length > 0) {
          void this.selectSubmission(this.submissionHistory[0].id);
        } else {
          this.clearSubmissionSelection();
          this.showScoreCard = true;
          this.render();
        }
        this.applyScoreCardAnimation();
        window.scrollTo({ top: 0, behavior: 'smooth' });
      });
    }

    if (this.elements.grChoose && this.elements.grFiles) {
      this.elements.grChoose.onclick = () => this.elements.grFiles.click();
      this.elements.grFiles.onchange = () => this.handleFileSelect();
    }

    if (this.elements.grSubmit) {
      this.elements.grSubmit.onclick = () => this.handleAnalysisSubmit();
    }

    if (this.elements.getVotedToggle) {
      this.elements.getVotedToggle.addEventListener('change', () => this.handleGetVotedToggleChange());
    }
  }

  private handleFileSelect(): void {
    const files = Array.from(this.elements.grFiles.files || []);
    const preview = this.elements.grPreview;
    
    if (this.lastUploadedPreviewUrl) {
      URL.revokeObjectURL(this.lastUploadedPreviewUrl);
      this.lastUploadedPreviewUrl = null;
    }
    
    while (preview.firstChild) {
      preview.removeChild(preview.firstChild);
    }
    
    files.slice(0, 3).forEach(file => {
      const img = document.createElement('img');
      img.className = 'w-full h-32 object-cover rounded-xl border border-gray-200 shadow-sm';
      img.src = URL.createObjectURL(file);
      preview.appendChild(img);
    });
    
    if (files[0]) {
      this.lastUploadedPreviewUrl = URL.createObjectURL(files[0]);
    }
    
    this.elements.grSubmit.disabled = files.length === 0;
  }

  private async loadData(): Promise<void> {
    try {
      const { data: { user }, error: authError } = await supabase.auth.getUser();

      if (authError || !user) {
        window.location.href = '/login';
        return;
      }

      this.currentUser = user;
      this.revealMainUiImmediate();

      // Use InstantLoad for immediate rendering
      const { profile: cachedProfile, cooldown: cachedCooldown } = await InstantLoad.load({
        includeCooldown: true,
        cooldownScope: 'analysis_tab'
      });
      
      if (cachedProfile) {
        console.log('Found cached profile data, rendering immediately');
        this.userProfile = cachedProfile as any;
        this.avatarSignedUrl = cachedProfile.avatar_signed_url || null;
        this.initVotePreference();
        this.render();
        void this.loadSubmissionHistory();
      }

      if (cachedCooldown) {
        this.cooldownCache = {
          timestamp: Date.now(),
          canAnalyze: !cachedCooldown.onCooldown
        };
      }

      // Background refresh
      const freshProfile = await InstantLoad.refresh({
        includeCooldown: true,
        cooldownScope: 'analysis_tab'
      });
      
      if (freshProfile) {
        this.userProfile = freshProfile as any;
        this.avatarSignedUrl = freshProfile.avatar_signed_url || null;
        
        const freshCooldown = InstantLoad.getCachedCooldown('analysis_tab');
        if (freshCooldown) {
          this.cooldownCache = {
            timestamp: Date.now(),
            canAnalyze: !freshCooldown.onCooldown
          };
        }

        this.isInitialLoad = false;
        this.initVotePreference();
        this.render();
        void this.loadSubmissionHistory();
      } else if (!this.userProfile) {
        throw new Error('Failed to load profile');
      }

    } catch (error) {
      console.error('Error loading data:', error);
      this.showError();
    }
  }

  private render(): void {
    if (!this.userProfile) return;

    const hasProfileScore = this.userProfile.ai_score != null;
    const hasTransientScore = this.transientAnalysis?.ai_score != null;
    const hasSelectedScore = this.selectedSubmissionDetail?.ai_score != null;
    const hasHistoryScore = this.submissionHistory.some((h) => h.ai_score != null);
    const hasAnyScore =
      hasProfileScore || hasTransientScore || hasSelectedScore || hasHistoryScore;
    const hasRenderableCard =
      hasAnyScore || this.submissionHistory.length > 0;

    const shouldShowScoreCard = hasRenderableCard && this.showScoreCard;
    this.toggleSections(shouldShowScoreCard);

    if (this.elements.analysisProCta) {
      const showProCta = shouldShowScoreCard && this.userProfile.is_pro_user !== true;
      this.elements.analysisProCta.classList.toggle('hidden', !showProCta);
    }

    // Render Score Card Content
    if (this.elements.scoreValue) {
        const activeScore = this.getActiveAnalysis().ai_score;
        const targetScore = activeScore != null ? activeScore : 0;
        
        if (activeScore != null) {
            // Only animate if it's NOT the initial load (to make it instant on load)
            // OR if the score has actually changed from what's currently displayed
            const currentVal = parseFloat(this.elements.scoreValue.textContent || '0');
            if (!this.isInitialLoad && currentVal !== targetScore) {
                this.animateValue(this.elements.scoreValue, currentVal, targetScore, 2000);
            } else {
                this.elements.scoreValue.textContent = targetScore.toFixed(1);
            }
        } else {
            this.elements.scoreValue.textContent = '-';
        }
    }
    
    if (this.elements.overallAssessment) {
        const activeAssessment = this.getActiveAnalysis().ai_assessment;
        this.elements.overallAssessment.textContent = activeAssessment 
            ? activeAssessment
            : 'Upload a photo to get your analysis...';
    }

    // Avatar: blob / submission thumb = direct; profile signed URL = ThumbHash first then full (like profile page)
    if (this.elements.scoreAvatar) {
      const activeAvatarUrl = this.getActiveAnalysis().avatarUrl;
      const img = this.elements.scoreAvatar;
      img.onerror = () => {
        img.src = '/anon.webp';
        img.onerror = null;
      };

      if (!activeAvatarUrl) {
        img.onerror = null;
        img.src = '/anon.webp';
      } else if (activeAvatarUrl.startsWith('blob:')) {
        img.src = activeAvatarUrl;
      } else if (this.showScoreCard && this.selectedSubmissionId && this.selectedSubmissionDetail) {
        img.src = activeAvatarUrl;
      } else {
        const thumb = this.currentUser
          ? resolveAvatarThumbhashDataUrl(this.currentUser.id, this.userProfile?.avatar_thumbhash ?? null)
          : null;
        if (thumb && activeAvatarUrl !== '/anon.webp') {
          img.src = thumb;
          requestAnimationFrame(() => {
            requestAnimationFrame(() => {
              const pre = new Image();
              pre.onload = () => {
                img.src = activeAvatarUrl;
              };
              pre.onerror = () => {
                img.src = thumb;
              };
              pre.src = activeAvatarUrl;
            });
          });
        } else {
          img.src = activeAvatarUrl;
        }
      }
    }
    
    // No Score Overlay
    if (this.elements.noScoreOverlay) {
        const activeScore = this.getActiveAnalysis().ai_score;
        this.elements.noScoreOverlay.classList.toggle('hidden', activeScore != null);
    }

    // Potential
    if (this.elements.potentialNudge) {
        let potentialGainValue = 0.8;
        if (hasAnyScore) {
            const analysis = this.getActiveAnalysis().ai_analysis_json || {};
            const rawPotential = analysis?.potentialScore;
            
            if (typeof rawPotential === 'number') {
                potentialGainValue = rawPotential;
            } else if (typeof rawPotential === 'string' && rawPotential.trim() !== '') {
                const parsed = parseFloat(rawPotential);
                if (!Number.isNaN(parsed)) potentialGainValue = parsed;
            }
            
            const activeScore = this.getActiveAnalysis().ai_score;
            if (activeScore !== null && activeScore !== undefined) {
                potentialGainValue = Math.min(10 - activeScore, potentialGainValue);
            }
        }
        
        if (this.elements.potentialNudgeValue) {
             this.elements.potentialNudgeValue.textContent = potentialGainValue.toFixed(1);
        }
        this.elements.potentialNudge.classList.toggle('potential-nudge-hidden', !hasAnyScore);
    }

    // Level-gated extras from basic submission JSON (same object the edge function returns as `analysis`)
    const xpForExtras = this.userProfile?.xp ?? 0;
    const levelForExtras = Math.floor(xpForExtras / 100) + 1;
    const analysisJsonForExtras = this.getActiveAnalysis().ai_analysis_json || {};

    // Best feature: locked strip below L3; full card at L3+ when data exists
    if (this.elements.analysisBestFeature) {
      const rawName = (analysisJsonForExtras as { keyPositiveFeature1_Name?: unknown }).keyPositiveFeature1_Name;
      const rawDesc = (analysisJsonForExtras as { keyPositiveFeature1_Description?: unknown }).keyPositiveFeature1_Description;
      const name = typeof rawName === 'string' ? rawName.trim() : '';
      const desc = typeof rawDesc === 'string' ? rawDesc.trim() : '';
      const showBestBody =
        levelForExtras >= 3 &&
        hasAnyScore &&
        (name.length > 0 || desc.length > 0);
      const showBestLocked = hasAnyScore && levelForExtras < 3;
      const showBestSection = showBestLocked || showBestBody;

      this.elements.analysisBestFeature.classList.toggle('hidden', !showBestSection);
      this.elements.analysisBestFeatureLocked?.classList.toggle('hidden', !showBestLocked);
      this.elements.analysisBestFeatureBody?.classList.toggle('hidden', !showBestBody);

      if (showBestBody) {
        if (this.elements.analysisBestFeatureName) {
          this.elements.analysisBestFeatureName.textContent = name || 'Your strongest feature';
        }
        if (this.elements.analysisBestFeatureDesc) {
          this.elements.analysisBestFeatureDesc.textContent = desc;
          this.elements.analysisBestFeatureDesc.classList.toggle('hidden', desc.length === 0);
        }
      }
    }

    // Scoring summary: locked strip below L4; full card at L4+ when internalReasoning exists
    if (this.elements.analysisScoringSummary) {
      const rawReasoning = (analysisJsonForExtras as { internalReasoning?: unknown }).internalReasoning;
      const reasoning = typeof rawReasoning === 'string' ? rawReasoning.trim() : '';
      const showScoringBody =
        levelForExtras >= 4 &&
        hasAnyScore &&
        reasoning.length > 0;
      const showScoringLocked = hasAnyScore && levelForExtras < 4;
      const showScoringSection = showScoringLocked || showScoringBody;

      this.elements.analysisScoringSummary.classList.toggle('hidden', !showScoringSection);
      this.elements.analysisScoringSummaryLocked?.classList.toggle('hidden', !showScoringLocked);
      this.elements.analysisScoringSummaryBody?.classList.toggle('hidden', !showScoringBody);
      if (this.elements.analysisScoringSummaryText) {
        this.elements.analysisScoringSummaryText.textContent = showScoringBody ? reasoning : '';
      }
    }

    // Render Upload Section State
    if (this.elements.firstAnalysisBadge) {
        this.elements.firstAnalysisBadge.classList.toggle('hidden', hasProfileScore);
    }
    
    if (this.elements.xpBonusBadge) {
        const isPublic = this.userProfile.profile_visibility === 'public';
        this.elements.xpBonusBadge.classList.toggle('hidden', !(isPublic && !hasProfileScore));
    }

    if (this.elements.uploadNotice) {
        this.elements.uploadNotice.classList.toggle('hidden', hasProfileScore);
    }
    
    if (this.elements.uploadPublicNotice) {
         const isPublic = this.userProfile.profile_visibility === 'public';
         this.elements.uploadPublicNotice.classList.toggle('hidden', !isPublic);
    }
    
    if (this.elements.lastAnalysisBtn) {
        this.elements.lastAnalysisBtn.classList.toggle('hidden', !hasRenderableCard);
    }
    
    if (this.elements.getVotedToggle) {
        const hasProfileAvatar = !!String(this.userProfile?.avatar_image_path || '').trim();
        const hideProfileToggle = !hasProfileAvatar;
        if (hideProfileToggle) {
            this.elements.getVotedToggle.checked = true;
            this.votePreference = true;
            if (this.elements.profilePicToggleContainer) {
                this.elements.profilePicToggleContainer.classList.add('hidden');
            }
        } else {
            this.elements.getVotedToggle.checked = this.votePreference ?? false;
            if (this.elements.profilePicToggleContainer) {
                this.elements.profilePicToggleContainer.classList.remove('hidden');
            }
        }
    }

    this.renderSubmissionHistory();
    this.checkCooldownStatus();
  }

  private clearSubmissionSelection(): void {
    this.selectedSubmissionId = null;
    this.selectedSubmissionDetail = null;
    this.selectedSubmissionThumbUrl = null;
  }

  /** Private bucket: anon client cannot sign; use Edge `sign-submission-images` (service role). */
  private async signThumbForPaths(fullPaths: string[]): Promise<Map<string, string | null>> {
    const out = new Map<string, string | null>();
    const unique = [...new Set(fullPaths.filter((p) => p && p.length > 0))];
    if (unique.length === 0) return out;
    try {
      const { data, error } = await supabase.functions.invoke('sign-submission-images', {
        body: { paths: unique },
      });
      if (error) {
        console.warn('sign-submission-images', error);
        return out;
      }
      const urls = (data as { signed_urls?: (string | null)[] })?.signed_urls || [];
      unique.forEach((p, i) => {
        const u = urls[i];
        out.set(p, typeof u === 'string' && u.length > 0 ? u : null);
      });
    } catch (e) {
      console.warn('signThumbForPaths', e);
    }
    return out;
  }

  private async signThumbForPath(fullPath: string): Promise<string | null> {
    const m = await this.signThumbForPaths([fullPath]);
    return m.get(fullPath) ?? null;
  }

  private async loadSubmissionHistory(): Promise<void> {
    if (!this.currentUser) return;
    try {
      const { data, error } = await supabase
        .from('user_submissions')
        .select('id, created_at, ai_score, image_paths, primary_thumbhash')
        .eq('user_id', this.currentUser.id)
        .order('created_at', { ascending: false })
        .limit(30);
      if (error) {
        console.error('user_submissions list', error);
        return;
      }
      const rows = data || [];
      const needSignFirst: string[] = [];
      const rowMeta: {
        row: (typeof rows)[0];
        first: string | undefined;
        hashB64: string | null;
        hashOk: boolean;
      }[] = [];
      for (const row of rows) {
        const paths = row.image_paths as string[] | null;
        const first = paths?.[0];
        const hashB64 =
          typeof (row as { primary_thumbhash?: unknown }).primary_thumbhash === 'string'
            ? (row as { primary_thumbhash: string }).primary_thumbhash
            : null;
        const hashOk = !!(hashB64 && thumbHashBase64ToDataUrl(hashB64));
        if (!hashOk && first) needSignFirst.push(first);
        rowMeta.push({ row, first, hashB64, hashOk });
      }
      const signedByPath = await this.signThumbForPaths(needSignFirst);
      const list: typeof this.submissionHistory = [];
      for (const { row, first, hashB64, hashOk } of rowMeta) {
        const thumb = !hashOk && first ? signedByPath.get(first) ?? null : null;
        list.push({
          id: row.id,
          created_at: row.created_at,
          ai_score: row.ai_score != null ? Number(row.ai_score) : null,
          thumbSignedUrl: thumb,
          thumbHashB64: hashB64,
        });
      }
      this.submissionHistory = list;
      this.render();
    } catch (e) {
      console.error('loadSubmissionHistory', e);
    }
  }

  private async selectSubmission(
    id: string,
    opts?: { prefetchedThumbUrl?: string | null }
  ): Promise<void> {
    this.transientAnalysis = null;
    this.selectedSubmissionId = id;
    try {
      const { data, error } = await supabase
        .from('user_submissions')
        .select('id, ai_score, ai_assessment, ai_analysis_json, image_paths')
        .eq('id', id)
        .single();
      if (error || !data) {
        console.error('user_submissions row', error);
        return;
      }
      const paths = (data.image_paths as string[]) || [];
      this.selectedSubmissionDetail = {
        ai_score: data.ai_score != null ? Number(data.ai_score) : null,
        ai_assessment: data.ai_assessment ?? null,
        ai_analysis_json: data.ai_analysis_json,
        image_paths: paths,
      };
      const first = paths[0];
      const prefetched = opts?.prefetchedThumbUrl;
      this.selectedSubmissionThumbUrl =
        prefetched && prefetched.length > 0
          ? prefetched
          : first
            ? await this.signThumbForPath(first)
            : null;
      this.showScoreCard = true;
      this.toggleSections(true);
      this.render();
    } catch (e) {
      console.error('selectSubmission', e);
    }
  }

  private renderSubmissionHistory(): void {
    const { analysisHistoryList, analysisHistoryEmpty } = this.elements;
    if (!analysisHistoryList || !analysisHistoryEmpty) return;
    if (this.submissionHistory.length === 0) {
      analysisHistoryList.innerHTML = '';
      analysisHistoryEmpty.classList.remove('hidden');
      return;
    }
    analysisHistoryEmpty.classList.add('hidden');
    analysisHistoryList.innerHTML = this.submissionHistory
      .map((row) => {
        const date = new Date(row.created_at).toLocaleString(undefined, {
          dateStyle: 'medium',
          timeStyle: 'short',
        });
        const scoreText = row.ai_score != null ? row.ai_score.toFixed(1) : '—';
        const selected = this.selectedSubmissionId === row.id;
        const border = selected
          ? 'border-purple-200 bg-gradient-to-br from-violet-50/90 to-fuchsia-50/50 ring-1 ring-purple-100'
          : 'border-slate-200 bg-white/90 hover:bg-slate-50';
        const thumbDataUrl = thumbHashBase64ToDataUrl(row.thumbHashB64);
        const thumbSrc = thumbDataUrl || row.thumbSignedUrl;
        const thumb = thumbSrc
          ? `<img src="${thumbSrc}" alt="" class="w-full h-full object-cover" loading="lazy" decoding="async" />`
          : `<div class="w-full h-full flex items-center justify-center text-slate-400 text-xs">—</div>`;
        return `<button type="button" role="listitem" data-submission-id="${row.id}" class="w-full flex items-center gap-3 p-3 rounded-xl border text-left transition-colors ${border}">
      <div class="w-14 h-14 rounded-lg overflow-hidden bg-slate-100 flex-shrink-0 border border-slate-200">${thumb}</div>
      <div class="flex-1 min-w-0">
        <div class="text-xs font-semibold text-slate-500">${date}</div>
        <div class="text-sm font-bold text-slate-800">Score <span class="bg-gradient-to-r from-violet-600 to-fuchsia-600 bg-clip-text text-transparent">${scoreText}</span></div>
      </div>
    </button>`;
      })
      .join('');
  }

  private toggleSections(showScoreCard: boolean): void {
    if (this.elements.scoreCard) {
      this.elements.scoreCard.classList.toggle('hidden', !showScoreCard);
      this.elements.scoreCard.style.display = showScoreCard ? 'block' : 'none';
    }
    if (this.elements.uploadSection) {
      this.elements.uploadSection.classList.toggle('hidden', showScoreCard);
      this.elements.uploadSection.style.display = showScoreCard ? 'none' : 'block';
    }
  }

  private getActiveAnalysis(): { ai_score: number | null; ai_assessment: string | null; ai_analysis_json: any; avatarUrl: string | null } {
    if (this.showScoreCard && this.selectedSubmissionId && this.selectedSubmissionDetail) {
      return {
        ai_score: this.selectedSubmissionDetail.ai_score,
        ai_assessment: this.selectedSubmissionDetail.ai_assessment,
        ai_analysis_json: this.selectedSubmissionDetail.ai_analysis_json,
        avatarUrl: this.selectedSubmissionThumbUrl,
      };
    }
    if (this.showScoreCard && this.transientAnalysis) {
      return {
        ai_score: this.transientAnalysis.ai_score,
        ai_assessment: this.transientAnalysis.ai_assessment,
        ai_analysis_json: this.transientAnalysis.ai_analysis_json,
        avatarUrl: this.lastUploadedPreviewUrl || this.avatarSignedUrl
      };
    }
    return {
      ai_score: this.userProfile?.ai_score ?? null,
      ai_assessment: this.userProfile?.ai_assessment ?? null,
      ai_analysis_json: this.userProfile?.ai_analysis_json,
      avatarUrl: this.avatarSignedUrl
    };
  }

  private initVotePreference(): void {
    if (this.votePreference !== null || !this.userProfile) return;

    const hasProfileAvatar = !!String(this.userProfile.avatar_image_path || '').trim();
    if (!hasProfileAvatar) {
      this.votePreference = true;
      return;
    }

    if (this.userProfile.ai_score == null) {
      this.votePreference = true;
      return;
    }

    try {
      const stored = localStorage.getItem('analysis:getVotedOn');
      if (stored === 'true' || stored === 'false') {
        this.votePreference = stored === 'true';
        return;
      }
    } catch {}
    const isPublic = this.userProfile.profile_visibility === 'public';
    if (isPublic) {
      this.votePreference = true;
    } else {
      this.votePreference = !!this.userProfile.is_vote_eligible;
    }
  }

  private handleGetVotedToggleChange(): void {
    if (!this.elements.getVotedToggle) return;
    this.votePreference = this.elements.getVotedToggle.checked;
    try {
      localStorage.setItem('analysis:getVotedOn', String(this.votePreference));
    } catch {}
  }

  private getVotePreference(): boolean {
    if (this.userProfile && !String(this.userProfile.avatar_image_path || '').trim()) {
      return true;
    }
    return this.votePreference ?? false;
  }

  private applyScoreCardAnimation(): void {
    if (!this.elements.scoreCard) return;
    this.elements.scoreCard.classList.remove('score-card-appear');
    void this.elements.scoreCard.offsetWidth;
    this.elements.scoreCard.classList.add('score-card-appear');
  }

  private animateValue(obj: HTMLElement, start: number, end: number, duration: number) {
    let startTimestamp: number | null = null;
    const step = (timestamp: number) => {
        if (!startTimestamp) startTimestamp = timestamp;
        const progress = Math.min((timestamp - startTimestamp) / duration, 1);
        
        // Easing function (easeOutQuart) for a natural slow-down effect
        const easeProgress = 1 - Math.pow(1 - progress, 4);
        
        const current = start + (easeProgress * (end - start));
        obj.textContent = current.toFixed(1);
        
        if (progress < 1) {
            window.requestAnimationFrame(step);
        } else {
            obj.textContent = end.toFixed(1);
        }
    };
    window.requestAnimationFrame(step);
  }

  private async checkCooldownStatus(): Promise<void> {
    const { grChoose, grSubmit, grStatus } = this.elements;
    if (!grChoose || !grSubmit || !grStatus) return;

    try {
        const now = Date.now();
        if (this.cooldownCache && (now - this.cooldownCache.timestamp) < ANALYSIS_TAB_COOLDOWN_TRUST_MS) {
            if (this.cooldownCache.canAnalyze) {
                this.setUploadEnabled('Upload another photo for analysis');
            } else {
                // InstantLoad.refresh already ran check_cooldown; reuse stored payload instead of duplicate invoke.
                const trusted = InstantLoad.getTrustedAnalysisTabCooldown();
                if (trusted && trusted.onCooldown) {
                    const hours = trusted.hoursRemaining ?? 0;
                    let minutesUntilReady =
                      typeof trusted.minutesUntilReady === 'number' ? trusted.minutesUntilReady : undefined;
                    const lastTs = this.userProfile?.last_analysis_time ?? null;
                    if ((minutesUntilReady == null || minutesUntilReady <= 0) && lastTs) {
                        const fromSpacing = this.minutesLeftFromAnalysisTabSpacing(lastTs);
                        if (fromSpacing > 0) minutesUntilReady = fromSpacing;
                    }
                    if ((minutesUntilReady == null || minutesUntilReady <= 0) && hours > 0) {
                        minutesUntilReady = hours * 60;
                    }
                    const statusText =
                        minutesUntilReady != null && minutesUntilReady > 0
                            ? `Try again in ${minutesUntilReady} minute${minutesUntilReady === 1 ? '' : 's'}.`
                            : 'Analysis on cooldown. Please wait.';
                    this.setUploadDisabled(statusText, hours, minutesUntilReady);
                    this.startCooldownTimer();
                } else {
                    await this.refreshCooldownFromServer();
                }
            }
            return;
        }

        await this.refreshCooldownFromServer();

    } catch (err) {
        console.error('Cooldown check error:', err);
        this.setUploadEnabled('Upload another photo for analysis');
    }
  }
  
  /**
   * Client fallback for countdown when the edge function omits minutes (stale deploy) —
   * uses the same 5‑minute spacing as `cooldown_scope: analysis_tab` on the server.
   */
  private minutesLeftFromAnalysisTabSpacing(lastIso: string | null | undefined): number {
      if (!lastIso) return 0;
      const elapsedMin = (Date.now() - new Date(lastIso).getTime()) / (60 * 1000);
      if (elapsedMin >= ANALYSIS_TAB_COOLDOWN_MINUTES) return 0;
      return Math.ceil(ANALYSIS_TAB_COOLDOWN_MINUTES - elapsedMin);
  }

  private async refreshCooldownFromServer(): Promise<void> {
      const { data, error } = await supabase.functions.invoke('track-goals', {
          body: { event: 'check_cooldown', cooldown_scope: 'analysis_tab' }
      });

      if (error) {
          let responseBody = '';
          try {
            const ctx = (error as { context?: Response }).context;
            if (ctx && typeof ctx.text === 'function') {
              responseBody = await ctx.clone().text();
            }
          } catch { /* ignore */ }
          console.error('track-goals check_cooldown failed:', error, responseBody || '');
          this.setUploadEnabled('Could not verify cooldown. Try again shortly.');
          return;
      }

      const payload = data as
        | {
            onCooldown?: boolean;
            hoursRemaining?: number;
            minutesUntilReady?: number;
            message?: string;
            lastAnalysisTime?: string | null;
          }
        | null;

      const canAnalyze = !payload?.onCooldown;

      this.cooldownCache = {
          timestamp: Date.now(),
          canAnalyze
      };

      if (canAnalyze) {
          this.setUploadEnabled('Upload another photo for analysis');
          this.updateTimerDisplay('Ready!', 0);
      } else {
          const hours = payload?.hoursRemaining ?? 0;
          let minutesUntilReady =
            typeof payload?.minutesUntilReady === 'number' ? payload.minutesUntilReady : undefined;
          const lastTs = payload?.lastAnalysisTime ?? this.userProfile?.last_analysis_time ?? null;
          if ((minutesUntilReady == null || minutesUntilReady <= 0) && lastTs) {
              const fromSpacing = this.minutesLeftFromAnalysisTabSpacing(lastTs);
              if (fromSpacing > 0) minutesUntilReady = fromSpacing;
          }
          if ((minutesUntilReady == null || minutesUntilReady <= 0) && hours > 0) {
              minutesUntilReady = hours * 60;
          }
          const statusText =
              payload?.message ||
              (minutesUntilReady != null && minutesUntilReady > 0
                  ? `Try again in ${minutesUntilReady} minute${minutesUntilReady === 1 ? '' : 's'}.`
                  : 'Analysis on cooldown. Please wait.');
          this.setUploadDisabled(statusText, hours, minutesUntilReady);
          this.startCooldownTimer();
      }
  }

  private setUploadEnabled(statusText: string): void {
    const { grChoose, grStatus, uploadCooldownTimer } = this.elements;
    grChoose.disabled = false;
    
    // Explicitly hide cooldown timer in enabled state
    if (uploadCooldownTimer) {
        uploadCooldownTimer.classList.add('hidden');
    }
    
    // Update content with new professional design
    grChoose.innerHTML = `
        <div class="w-14 h-14 bg-white rounded-full shadow-sm border border-slate-200 flex items-center justify-center text-slate-600 mb-3 group-hover:scale-110 group-hover:text-slate-800 transition-all duration-300">
            <i class="fas fa-cloud-arrow-up text-xl"></i>
        </div>
        <span class="text-slate-800 font-bold text-sm">Tap to Select Photos</span>
    `;
    
    grChoose.className = 'group w-full relative flex flex-col items-center justify-center p-8 border-2 border-dashed border-slate-300 rounded-2xl bg-slate-50/80 hover:bg-slate-100/80 hover:border-slate-400 transition-all duration-300 focus:outline-none focus:ring-4 focus:ring-slate-200 active:scale-[0.99]';
    
    grStatus.textContent = statusText;
    // Don't call updateTimerDisplay here as it might re-show hidden elements if not careful.
    // We've already hidden the timer above.
  }

  private setUploadDisabled(
    statusText: string,
    hoursRemaining: number = 0,
    minutesUntilReady?: number
  ): void {
    const { grChoose, grSubmit, grStatus } = this.elements;
    grChoose.disabled = true;
    grSubmit.disabled = true;
    
    // Update content for disabled state
    grChoose.innerHTML = `
        <div class="w-14 h-14 bg-gray-100 rounded-full flex items-center justify-center text-gray-400 mb-3">
            <i class="fas fa-clock text-xl"></i>
        </div>
        <span class="text-gray-500 font-bold text-sm">Analysis on cooldown...</span>
    `;
    
    grChoose.className = 'w-full relative flex flex-col items-center justify-center p-8 border-2 border-dashed border-gray-200 rounded-2xl bg-gray-50 cursor-not-allowed';
    
    grStatus.textContent = statusText;
    const totalMinutes =
      minutesUntilReady != null
        ? minutesUntilReady
        : hoursRemaining > 0
          ? hoursRemaining * 60
          : 0;
    this.updateTimerDisplay('Next analysis available in:', totalMinutes);
  }

  private updateTimerDisplay(_message: string, totalMinutes: number): void {
    const { uploadCooldownTimer } = this.elements;
    
    if (!uploadCooldownTimer) return;

    if (totalMinutes <= 0) {
        // Ready state: Hide the timer entirely. The upload button is the indicator.
        uploadCooldownTimer.classList.add('hidden');
    } else {
        // Cooldown state: Compact, professional design
        uploadCooldownTimer.classList.remove('hidden');
        
        // Minimalist design: Text with icon, no heavy background
        uploadCooldownTimer.className = 'mt-4 flex flex-col items-center justify-center animate-fade-in';

        const hours = Math.floor(totalMinutes / 60);
        const mins = totalMinutes % 60;
        const label =
            hours > 0 ? (mins > 0 ? `${hours}h ${mins}m` : `${hours}h`) : `${mins}m`;
        
        uploadCooldownTimer.innerHTML = `
            <div class="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-gray-100 text-gray-600 font-medium text-sm border border-gray-200 shadow-sm">
                <i class="fas fa-hourglass-half text-gray-500"></i>
                <span>Next analysis available in ${label}</span>
            </div>
            <p class="text-xs text-gray-400 mt-2">Analysis limits ensure high quality results.</p>
        `;
    }
  }
  
  private startCooldownTimer(): void {
      if ((this as any)._cooldownInterval) clearInterval((this as any)._cooldownInterval);
      
      const check = () => this.refreshCooldownFromServer();
      (this as any)._cooldownInterval = setInterval(check, 60000); 
  }

  private async handleAnalysisSubmit(): Promise<void> {
    if (!this.currentUser) return;
    
    const { grFiles, grSubmit, grStatus } = this.elements;
    const files = Array.from(grFiles.files || []);
    if (files.length === 0) return;
    const useForVoting = this.getVotePreference();
    this.transientAnalysis = null;
    this.clearSubmissionSelection();
    this.showScoreCard = false;

    try {
        const { data, error } = await supabase.functions.invoke('track-goals', {
          body: { event: 'analysis', cooldown_scope: 'analysis_tab' }
        });
        if (error || (data && data.error)) {
             const hours = data?.hoursRemaining || 0;
             const minutesUntilReady =
               typeof data?.minutesUntilReady === 'number' ? data.minutesUntilReady : undefined;
             this.setUploadDisabled(data?.message || 'Cooldown active', hours, minutesUntilReady);
             return;
        }
    } catch (e) {
        console.error(e);
        return;
    }

    grSubmit.disabled = true;
    grStatus.textContent = '';

    /** Match DOM to “upload” under the overlay so we never flash a stale score card. */
    this.render();

    const overlayStartedAt = performance.now();
    this.showAnalysisProcessingOverlay();
    const loadingPromise = startAnalysisLoading();
    setAnalysisLoadingMessage('Preparing photos…');

    try {
        const { uploadUserImages } = await import('./utils/supabase');
        const { paths, primaryThumbhash } = await uploadUserImages(files);
        if (primaryThumbhash && this.currentUser?.id) {
          setAvatarThumbhashForUser(this.currentUser.id, primaryThumbhash);
        }

        setAnalysisLoadingMessage('Analyzing images…');

        const { data: aiData, error: aiErr } = await supabase.functions.invoke('create-basic-submission-noemail', {
            body: {
                image_urls: paths,
                chosen_tier: 'basic',
                skip_profile_update: !useForVoting,
                reset_people_score: useForVoting,
                ...(primaryThumbhash ? { primary_image_thumbhash: primaryThumbhash } : {}),
            }
        });

        if (aiErr) throw aiErr;

        const userSubmissionId = (aiData as { user_submission_id?: string })?.user_submission_id;

        if (!useForVoting) {
            const analysis = (aiData as any)?.analysis || null;
            let aiScore = (aiData as any)?.ai_score ?? null;
            if (aiScore == null && typeof analysis?.facialAnalysisScore === 'string') {
                const part = analysis.facialAnalysisScore.split('/')[0]?.trim();
                const n = Number(part);
                if (!Number.isNaN(n)) aiScore = Math.round(n * 10) / 10;
            }
            const aiAssessment = (aiData as any)?.ai_assessment
                ?? analysis?.overallFacialAssessment
                ?? analysis?.overall_assessment
                ?? null;

            grFiles.value = '';
            while (this.elements.grPreview.firstChild) this.elements.grPreview.removeChild(this.elements.grPreview.firstChild);

            if (userSubmissionId) {
                await this.loadSubmissionHistory();
                const signedThumbs = (aiData as { signed_thumb_urls?: string[] })?.signed_thumb_urls;
                await this.selectSubmission(userSubmissionId, {
                  prefetchedThumbUrl: signedThumbs?.[0] ?? null,
                });
            } else {
                this.transientAnalysis = {
                    ai_score: typeof aiScore === 'number' ? aiScore : null,
                    ai_assessment: aiAssessment || null,
                    ai_analysis_json: analysis,
                };
                this.showScoreCard = true;
                this.render();
            }
            this.applyScoreCardAnimation();
            await this.awaitDoubleFrame();
            await this.finalizeAnalysisLoaderSuccess(loadingPromise, overlayStartedAt);

            grStatus.textContent = 'Ready to upload';
            InstantLoad.clearCooldownCache('default');
            return;
        }

        const prevAiScore = this.userProfile?.ai_score ?? null;
        const prevUpdatedAt = this.userProfile?.updated_at;

        const updatedProfile = await this.waitForScoresUpdate(prevAiScore, prevUpdatedAt);

        if (updatedProfile) {
            this.userProfile = updatedProfile as any;
            const refreshed = await InstantLoad.refresh({
              includeCooldown: true,
              cooldownScope: 'analysis_tab',
              forceRefresh: true,
            });
            if (refreshed) {
              this.userProfile = refreshed as any;
              this.avatarSignedUrl = refreshed.avatar_signed_url || null;
            } else {
              InstantLoad.saveProfile(this.userProfile as any);
              this.avatarSignedUrl = this.userProfile?.avatar_signed_url ?? null;
            }

            this.showScoreCard = true;
            this.render();
            this.applyScoreCardAnimation();

            grFiles.value = '';
            while (this.elements.grPreview.firstChild) this.elements.grPreview.removeChild(this.elements.grPreview.firstChild);

            grStatus.textContent = 'Ready to upload';

            if (prevAiScore == null && this.userProfile?.profile_visibility === 'public') {
                this.awardXpBonus();
            }
            void this.loadSubmissionHistory();
            InstantLoad.clearCooldownCache('default');

            await this.awaitDoubleFrame();
            await this.finalizeAnalysisLoaderSuccess(loadingPromise, overlayStartedAt);
        } else {
            cancelAnalysisLoading();
            await loadingPromise;
            this.hideAnalysisProcessingOverlay();
            grStatus.textContent = 'Still updating… check back shortly.';
            setTimeout(() => {
                void this.loadData();
            }, 5000);
        }
    } catch (error) {
        console.error('Analysis failed:', error);
        cancelAnalysisLoading();
        await loadingPromise;
        this.hideAnalysisProcessingOverlay();
        grStatus.textContent = 'Failed. Please try again.';
        grSubmit.disabled = false;
    }
  }

  /** Two rAFs so mobile Safari paints score card before the loader is removed. */
  private async awaitDoubleFrame(): Promise<void> {
    await new Promise<void>((r) =>
      requestAnimationFrame(() => requestAnimationFrame(() => r()))
    );
  }

  private async finalizeAnalysisLoaderSuccess(
    loadingPromise: Promise<void>,
    overlayStartedAt: number
  ): Promise<void> {
    completeAnalysisLoading();
    await loadingPromise;
    await new Promise<void>((r) =>
      setTimeout(r, Math.max(0, 200 - (performance.now() - overlayStartedAt)))
    );
    await this.awaitDoubleFrame();
    this.hideAnalysisProcessingOverlay();
  }

  private showAnalysisProcessingOverlay(): void {
    try {
      window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
    } catch {
      window.scrollTo(0, 0);
    }
    this.elements.loadingState.classList.add('analysis-loading--open');
    this.elements.loadingState.setAttribute('aria-busy', 'true');
    this.elements.loadingState.setAttribute('aria-hidden', 'false');
    document.body.classList.add('overflow-hidden');
    void this.elements.loadingState.offsetWidth;
  }

  private hideAnalysisProcessingOverlay(): void {
    this.elements.loadingState.classList.remove('analysis-loading--open');
    this.elements.loadingState.setAttribute('aria-busy', 'false');
    this.elements.loadingState.setAttribute('aria-hidden', 'true');
    document.body.classList.remove('overflow-hidden');
  }
  
  private async awardXpBonus(): Promise<void> {
      try {
          await supabase.from('user_profiles').update({ 
              xp: 50, // Simplified
              updated_at: new Date().toISOString() 
          }).eq('user_id', this.currentUser.id);
      } catch {}
  }

  private async waitForScoresUpdate(prevScore: number | null, prevUpdated?: string): Promise<UserProfile | null> {
      const start = Date.now();
      while (Date.now() - start < 90000) {
          const { data } = await supabase
            .from('user_profiles')
            .select('*')
            .eq('user_id', this.currentUser.id)
            .single();
            
          if (data) {
              const newScore = data.ai_score;
              const newUpdated = data.updated_at;
              
              if (prevScore == null ? newScore != null : newScore !== prevScore) return data;
              if (prevUpdated && newUpdated && new Date(newUpdated) > new Date(prevUpdated)) return data;
          }
          await new Promise(r => setTimeout(r, 3000));
      }
      return null;
  }
  
  /** Show main analysis UI immediately (no full-page loading overlay). */
  private revealMainUiImmediate(): void {
    this.elements.loadingState.classList.remove('analysis-loading--open');
    this.elements.loadingState.setAttribute('aria-busy', 'false');
    this.elements.loadingState.setAttribute('aria-hidden', 'true');
    document.body.classList.remove('overflow-hidden');
    this.elements.analysisContent.classList.remove('hidden');
    this.elements.analysisContent.style.display = 'block';
    this.elements.errorState.classList.add('hidden');
  }

  private showError() {
    document.body.classList.remove('overflow-hidden');
    this.elements.loadingState.classList.remove('analysis-loading--open');
    this.elements.loadingState.setAttribute('aria-busy', 'false');
    this.elements.loadingState.setAttribute('aria-hidden', 'true');
    this.elements.analysisContent.classList.add('hidden');
    this.elements.errorState.classList.remove('hidden');
  }
}

document.addEventListener('DOMContentLoaded', () => {
  setupNavPrefetch();
  new AnalysisPage();
});
