import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
import { View, Text, ActivityIndicator, NativeModules } from 'react-native';
import { checkVideoService, ensureFreshConfig, getBaseURL, loadConfigLocal, refreshConfig, NETWORK_ERROR_SAMPLE_RATE } from './service';
import { CheckVideoData, SDKProps, ViewpayHandle } from './type';
import { STEP_CHECKVIDEO_OK, STEP_COMPLETED, STEP_CONFIG_OK, ERROR_STEP, STEP_INITIAL, ERROR_CADRE, MESSAGE_HIDE_BTN_CLOSE_SDK_MOBILE, MESSAGE_SUCCES_SDK_MOBILE, MESSAGE_SHOW_BTN_CLOSE_SDK_MOBILE, MESSAGE_ID_EDITOR, MESSAGE_ERROR_SDK_MOBILE, MESSAGE_CLICK_CTA2_SDK_MOBILE } from './constants';
import Cadre from './cadre';
import { isTabletDevice } from './tools';
import { checkCappingNBRAccess, inscrementDataCaping, putLocalData,getVPUID, getNBView } from './localdata';
import { styles } from "./style";
import { captureException, tryHandleWebViewErrorMessage } from './sentry';


const Viewpay = forwardRef<ViewpayHandle, SDKProps>((props, ref) => {

    const [uniqueId, setUniqueId] = useState("");
    const [step, setStep] = useState(STEP_INITIAL);
    const [showASQ, setShowASQ] = useState(false);
    const [urlAsq, setUrlASQ] = useState("");
    const [checkVideoData, setCheckVideoData] = useState<CheckVideoData>();
    const [isCompleted, setIsCompleted] = useState(false);
    const [isActif, setIsActif] = useState(true);
    const [isHiddenVP, setHiddenVP] = useState(false);
    const [maxVuePerMonth,setMaxVuePerMonth] = useState(0);
    const [labelValidate, setLabelValidate] = useState('');
    const [log, setLog] = useState("");

    // Miroirs synchrones de step et maxVuePerMonth. Le handle exposé par
    // useImperativeHandle capture les valeurs du render courant : une app hôte
    // qui appelle checkVideo() de façon synchrone depuis readyVP() lirait donc
    // un step encore à STEP_INITIAL (React n'a pas re-rendu), tomberait sur la
    // garde, et ni checkVideoSuccesVP ni checkVideoErrorVP ne partirait — un
    // bouton de paywall resterait bloqué dans son état initial. Ces refs
    // rendent le SDK insensible au moment de l'appel.
    const stepRef = useRef(STEP_INITIAL);
    const maxVuePerMonthRef = useRef(0);

    const updateStep = (value: number) => {
      stepRef.current = value;
      setStep(value);
    };

    const updateMaxVuePerMonth = (value: number) => {
      maxVuePerMonthRef.current = value;
      setMaxVuePerMonth(value);
    };
    
    useImperativeHandle(ref, () => ({
      checkVideo,
      openCadre,
      getCappingRestant,
      /** @deprecated typo alias kept for 1.0.12 consumers — use getCappingRestant */
      getCappignRestant: getCappingRestant,
    }));

    const checkVideo = async () => {
      setLog("checking video.....");
      if(stepRef.current<=STEP_INITIAL){
        props.errorVP(ERROR_STEP,"Faire une checkVideo alorsque le step est : "+stepRef.current);
        return;
      }
      // Rattrapage réseau volontairement NON bloquant. La config locale (cache
      // ou défauts) est déjà exploitable : faire attendre l'appelant ici
      // retarderait checkVideoSuccesVP / checkVideoErrorVP, dont les apps hôtes
      // se servent pour afficher ou masquer leur bouton dans le paywall. Le
      // résultat bénéficie aux appels suivants.
      ensureFreshConfig(props.siteId).then((refreshed) => {
        if (refreshed !== null) updateMaxVuePerMonth(refreshed);
      });

      let ifCappingAccesOK = await checkCappingNBRAccess(maxVuePerMonthRef.current);
      if (!ifCappingAccesOK) {
        if (props.checkVideoErrorVP) props.checkVideoErrorVP();
        return;
      }

      checkVideoService(props.siteId,uniqueId,{articleTitle:props.articleTitle,articleUrl:props.articleUrl,consentString:props.consentString,gdprApplies:props.gdprApplies}).then((ret: CheckVideoData) => {
        setLog("result checkVideo :" + ret);
        setCheckVideoData(ret);
        setLabelValidate(ret.labelValidate);
        updateStep(STEP_CHECKVIDEO_OK);
        if (ret.campIds.length === 0 && ret.campFreeIds.length === 0) {
          if (props.checkVideoErrorVP) props.checkVideoErrorVP();
        } else {
          if (props.checkVideoSuccesVP) props.checkVideoSuccesVP();
        }
      }).catch(e => {
        setLog("ERROR "+ e.message);
        const isNetworkError =
          e instanceof Error &&
          (e.message === 'Network Error' || e.message.includes('timeout'));
        captureException(e, {
          siteId: props.siteId,
          vpuid: uniqueId,
          step: stepRef.current,
          extra: { phase: 'checkVideoService' },
          ...(isNetworkError ? { sampleRate: NETWORK_ERROR_SAMPLE_RATE } : {}),
        });
      });
    };

    const openCadre = async () => {
      if(stepRef.current<STEP_CHECKVIDEO_OK){
        props.errorVP(ERROR_STEP,"Ouvrir cadre alorsque le step est : "+stepRef.current);
        return;
      }
      // Pas d'attente réseau ici non plus : getBaseURL() est déjà renseigné par
      // la couche locale, et retarder l'ouverture du cadre se verrait à l'écran.
      ensureFreshConfig(props.siteId);
      loadAsq(checkVideoData,props.siteId);
    };

    const getCappingRestant = async () => {
      let nbr = await getNBView();
      return [nbr,maxVuePerMonthRef.current];
    }
    
    const loadAsq = (checkData? : CheckVideoData,siteId?: string) => {
      let urlAsq = getBaseURL() + "/adSelectorDirect.htm?id=" + siteId + "&hostId="+uniqueId+"&me&sdk=1";
      urlAsq += "&price=0.5&mobile&noInterface&nps=-1&active_cookie=false&cMarketing=false&cUserAgentVpuid=true&rDebug=false&sTodb=true&recordStatData=false&consent_cookie=1";
      if (checkData) {
        urlAsq += "&cvid=" + checkData.cvid;
        urlAsq += "&campVP=" + JSON.stringify(checkData.campIds);
        urlAsq += "&campFree=" + JSON.stringify(checkData.campFreeIds);
      }
      // articleId / articleTitle de l'app hôte → mobArtId / mobArtTitle, lus par
      // AdSelectorQuickController uniquement si le flag mobile `me` est présent (c'est le
      // cas ci-dessus). encodeURIComponent suffit : le container fait le décodage standard
      // (pas de double-encodage, contrairement à pageTitle/urlEditor du checkVideo).
      if (props.articleId) {
        urlAsq += "&mobArtId=" + encodeURIComponent(props.articleId);
      }
      if (props.articleTitle) {
        urlAsq += "&mobArtTitle=" + encodeURIComponent(props.articleTitle);
      }
      // Consentement RGPD propagé jusqu'à la DIFFUSION : les macros VAST/VPAID
      // [consent_string] / [gdpr_applies] sont remplies par FlexServiceFacadeImpl
      // (getListVideo) à l'étape adSelectorDirect, en relisant ces params depuis
      // CETTE requête. Le consentement envoyé au checkVideo (stateless) n'est pas
      // propagé → il faut le renvoyer ici. getParameter standard → encode simple.
      if (props.consentString) {
        urlAsq += "&consent_string=" + encodeURIComponent(props.consentString);
      }
      if (props.gdprApplies !== undefined) {
        urlAsq += "&gdpr_applies=" + (props.gdprApplies ? "1" : "0");
      }
      urlAsq += "&typeDevice=" + (isTabletDevice() ? "1" : "2");

      setUrlASQ(urlAsq);
      setShowASQ(true);
    };

    const onMessage = (data: any) => {
      setLog("::::::::>>> onMessage data=" + data);

      if (tryHandleWebViewErrorMessage(data, { siteId: props.siteId, vpuid: uniqueId, step: stepRef.current })) {
        return;
      }

      if (data === MESSAGE_SUCCES_SDK_MOBILE) {
        updateStep(STEP_COMPLETED);
        setIsCompleted(true);
        inscrementDataCaping().then(()=>{
          if (props.completeAdsVP) props.completeAdsVP();
        });
        return;
      }
      else if (data === MESSAGE_HIDE_BTN_CLOSE_SDK_MOBILE) {
        setIsActif(true);
        return;
      }
      else if (data === MESSAGE_SHOW_BTN_CLOSE_SDK_MOBILE) {
        setIsActif(false);
        return;
      }
      else if (data.includes(MESSAGE_ID_EDITOR)) {
        putLocalData("data_capping", data);
      }
      else if (data === MESSAGE_ERROR_SDK_MOBILE){
        props.errorVP(ERROR_CADRE,"")
      }
    };

    const closeCadre = (param: any) => {
      setShowASQ(false);
    };

    const openContenu = (param: any) => {
      setHiddenVP(true);
    };

    const fetchUniqueId = async () => {
      const id = await getVPUID();
      setUniqueId(id+"");
    };

    // Volontairement dépendant du seul siteId, et plus du step : l'ancienne
    // version se gardait sur `step === STEP_INITIAL` et, comme l'échec faisait
    // quand même passer le step à STEP_CONFIG_OK, la config n'était jamais
    // retentée — une coupure réseau au mount condamnait toute la session.
    useEffect(() => {
      const controller = new AbortController();
      let cancelled = false;

      fetchUniqueId();

      (async () => {
        // Couche locale : cache sinon défauts compilés. Ne dépend pas du
        // réseau, donc le SDK est utilisable tout de suite et `readyVP()` dit
        // la vérité au lieu d'annoncer un état prêt sur une config absente.
        const local = await loadConfigLocal(props.siteId!);
        if (cancelled) return;
        updateMaxVuePerMonth(local);
        updateStep(STEP_CONFIG_OK);
        props.readyVP();

        // Couche réseau : override en tâche de fond, sans bloquer personne.
        const fresh = await refreshConfig(props.siteId!, controller.signal);
        if (cancelled || fresh === null) return;
        updateMaxVuePerMonth(fresh);
      })();

      // Sans cet abort, la boucle de retry (6 s au total) survivait au
      // démontage et finissait par remonter une erreur pour un composant qui
      // n'existait plus — une part du volume observé côté Sentry.
      return () => {
        cancelled = true;
        controller.abort();
      };
    }, [props.siteId]);
  
    return (
      <>
        {props.withLog && (<Text>LOG : {log}</Text>)}
        {!isHiddenVP && (
          <View style={styles.containerViewpay}>
            {showASQ && (
              <Cadre
                isValidVisible={isCompleted}
                isCloseVisible={!isActif}
                onMessage={onMessage}
                onClose={closeCadre}
                onOpenContenu={openContenu}
                uri={urlAsq}
                labelValidate={labelValidate}
              />
            )}
          </View>
        )}
      </>
    );
});

export default Viewpay;
