#' Corrected sandwich inference for the marginal HR in the IPW Cox model for the standard setting of independent data
#'
#' Inference for the marginal hazard ratio (HR) in the inverse probability weighted (IPW) Cox model for the standard setting, assuming independence among observations (rows). Corrected sandwich variance (CSV) estimation method is used to estimate the variance of the estimated log HR and to construct confidence intervals for the HR. 
#'@param data A data set to be analyzed in the form of R data frame.
#'@param indA A column name indicating the treatment variable (1 = treated, 0 = untreated).
#'@param indX A vector of column names specifying the covariates to be included in the propensity score model. Any covariate with the same value across all observations will be excluded, as it does not contribute to propensity score estimation.
#'@param indStatus A column name indicating the non-censoring status (1 = event, 0 = censored).
#'@param indTime A column name indicating the outcome variable, i.e., min(true event time, censoring time).
#'@param weightType A character string specifying the type of weight ("ATE" when the estimand is the average treatment effect, or "ATT" when the estimand is the average treatment effect in the treated); the default is "ATE".
#'@param ties A character string specifying the method ("efron","breslow", or "exact") used to handle tied event times for point estimation; the default is "breslow". For variance estimation, the Breslow method is used to handle tied event times. 
#'@param confidence A confidence level between 0 and 1; the default is 0.95 corresponding to a 95 per cent confidence interval.
#'@return A matrix of inference results from the inverse probability weighted Cox model for independent data. The columns report the estimated log hazard ratio, the corrected sandwich standard error of the log hazard ratio, the estimated hazard ratio, the confidence interval for the hazard ratio computed using the corrected sandwich variance, and the p-value, respectively. Each row corresponds to a weight type. 
#'@import survival
#'@import stats
#'
#'@examples
#'#simulate a data set with a marginal hazard ratio of 1.5
#'set.seed(100)
#'n=700
#'T0=rexp(n, rate=0.01)
#'ZZZ=rnorm(n)
#'X1= 0.5*(T0+0.2)/(T0+1)+0.3*ZZZ
#'X2= 1/log(1.3*T0+3)-0.3*ZZZ
#'X3= rbinom(n,1,0.3+0.5/(T0+1))  
#'A=rbinom(n,1,1/(1+exp(0.53+X1-X2-X3)))
#'Ttime <- T0*exp(-log(1.5)*A)
#'rateC=0.0005
#'C <- rexp(n, rate=rateC)
#'time <- pmin(Ttime, C)
#'status <- as.numeric(Ttime <= C)
#'da=data.frame(id=1:n,time=time,status=status,A=A,X1=X1,X2=X2,X3=X3)
#'head(da)
#'#corrected sandwich variance-based inference results for the marginal hazard ratio 
#'ipwCoxInd(data=da,indA="A",indX=c("X1","X2","X3"),indStatus="status",indTime="time")
#'
#'@export
ipwCoxInd<-function(data,indA,indX,indStatus,indTime,weightType="ATE",ties="breslow",confidence=0.95){
  data=data[,c(indA,indX,indStatus,indTime)]
  if(anyNA(data)==TRUE) {
    warning('Missing values were detected in at least one variable specified by `indA`, `indX`, `indStatus`, or `indTime`. Rows with missing values for these variables were excluded from the analysis.')
    data <- na.omit(data)
  }
  
  dfXs=as.data.frame(data[,indX])
  
  dfXs <- as.data.frame(dfXs[, sapply(dfXs, function(x) length(unique(x)) > 1)])
  
  if(ncol(dfXs)==0) stop('All covariates specified by `indX` have been dropped because they have the same value across all observations and therefore do not contribute to propensity score estimation. Consider including additional covariates in `indX` as appropriate.')
  
  covX <- as.matrix(model.matrix(~ ., dfXs))
  
  nX=ncol(covX)
  colnames(covX) <- c("psIntercept", paste0("psCovariateX", 1:(nX-1)))
  
  covX0 <- as.matrix(covX[, -1])
  
  
  A=data[,indA]
  time=data[,indTime]
  status=data[,indStatus]
  
  n=length(A)
  id=1:n
  
  dat=data.frame(id=id,A=A,time=time,status=status)
  
  psmd=glm(A~.,family="binomial",data=as.data.frame(cbind(A,covX0)))
  psfit=predict(psmd, type = "response")
  
  
  if (!(weightType %in% c("ATE", "ATT"))) {
    stop("Invalid value for `weightType`. Please set it to either \"ATE\" or \"ATT\".")
    
  } else if (weightType=="ATE"){
    
    dat$wt=A/psfit+(1-A)/(1-psfit)
    prevA=mean(A)
    dat$swt=prevA*A/psfit+(1-prevA)*(1-A)/(1-psfit)
    
    fit <- coxph(Surv(time, status) ~ A+cluster(id), weights=dat$wt,data=dat, ties=ties)
    logHR=fit$coefficients
    
    
    fits <- coxph(Surv(time, status) ~ A+cluster(id), weights=dat$swt,data=dat, ties=ties)
    logHRs=fits$coefficients
    
    
    HRest=exp(logHR)
    HRests=exp(logHRs)
    
    
    dgvec=-A*(1-psfit)/psfit+(1-A)*psfit/(1-psfit)
    dgvecS=-prevA*A*(1-psfit)/psfit+(1-prevA)*(1-A)*psfit/(1-psfit)
    
    
    WdevR=t(as.matrix(covX * rep(dgvec, nX))) 
    
    WdevRS=t(as.matrix(covX * rep(dgvecS, nX)))
    WfS=A/psfit-(1-A)/(1-psfit)
  
    eventid=which(status==1)
    eventimes=unique(time[eventid])
    
    RScol5=eventimes
    RScol3=rep(0,length(eventimes))
    RScol4=RScol3
    RScol1=RScol3
    RScol2=RScol3
    
    RScol5s=eventimes
    RScol3s=RScol3
    RScol4s=RScol3
    RScol1s=RScol3
    RScol2s=RScol3
    
    
    for (i in 1:length(eventimes)){
      idc=which(dat$time==eventimes[i]&dat$status==1)
      RD1=dat[idc,]
      
      RScol1[i]=sum(RD1$wt*RD1$A)
      RScol2[i]=sum(RD1$wt)
      
      RSind=which(dat$time>=eventimes[i])
      RS1=dat[RSind,]
      
      RScol3[i]=sum(RS1$wt*RS1$A)
      RScol4[i]=sum(RS1$wt*(1-RS1$A))
      
      RScol1s[i]=sum(RD1$swt*RD1$A)
      RScol2s[i]=sum(RD1$swt)
      RScol3s[i]=sum(RS1$swt*RS1$A)
      RScol4s[i]=sum(RS1$swt*(1-RS1$A))   
    }
    
    risksetFull=data.frame(sumAiWi=RScol1,sumWi=RScol2,sumW1=RScol3,sumW0=RScol4,time=RScol5)
    risksetFulls=data.frame(sumAiWi=RScol1s,sumWi=RScol2s,sumW1=RScol3s,sumW0=RScol4s,time=RScol5s)
    
    
    gg=rep(0,n)
    ggS=gg
    
    
    A11x=rep(0,n)
    A12x=matrix(0,nX,n)
    A11xS=rep(0,n)
    A12xS=matrix(0,nX,n)
    A13xS=rep(0,n)
    
    
    for (x in eventid){
      ind1=which(risksetFull$time==dat$time[x])
      vv=(risksetFull$sumW1*HRest)/(risksetFull$sumW1*HRest+risksetFull$sumW0)
      gg[x]=dat$wt[x]*(dat$A[x]- vv[ind1])
      
      ind1=which(risksetFulls$time==dat$time[x])
      vv=(risksetFulls$sumW1*HRests)/(risksetFulls$sumW1*HRests+risksetFulls$sumW0)
      ggS[x]=dat$swt[x]*(dat$A[x]- vv[ind1])
      
      
      idrs=which(dat$time>=dat$time[x])
      s0x=sum(dat$wt[idrs]*exp(A[idrs]*logHR))
      s1x=sum(dat$wt[idrs]*exp(A[idrs]*logHR)*A[idrs])
      A11x[x]=dat$wt[x]*(s1x/s0x-s1x^2/(s0x^2))
      A12a=(A[x]-s1x/s0x)*WdevR[,x]
      
      s0xS=sum(dat$swt[idrs]*exp(A[idrs]*logHRs))
      s1xS=sum(dat$swt[idrs]*exp(A[idrs]*logHRs)*A[idrs])
      A11xS[x]=dat$swt[x]*(s1xS/s0xS-s1xS^2/(s0xS^2))
      A12aS=(A[x]-s1xS/s0xS)*WdevRS[,x]
      A13aS=(A[x]-s1xS/s0xS)*WfS[x]
      
      if (length(idrs)==1){
        A12c=(WdevR[,idrs]*exp(A[idrs]*logHR))*s1x
        A12d=WdevR[,idrs]*(exp(A[idrs]*logHR)*A[idrs])
        
        A12cS=(WdevRS[,idrs]*exp(A[idrs]*logHRs))*s1xS
        A12dS=WdevRS[,idrs]*(exp(A[idrs]*logHRs)*A[idrs])
        A13cS=(WfS[idrs]*exp(A[idrs]*logHRs))*s1xS
        A13dS=WfS[idrs]*(exp(A[idrs]*logHRs)*A[idrs])
      } else{
        A12c=(WdevR[,idrs]%*%exp(dat$A[idrs]*logHR))*s1x
        A12d=WdevR[,idrs]%*%(exp(dat$A[idrs]*logHR)*dat$A[idrs])
        
        A12cS=(WdevRS[,idrs]%*%exp(dat$A[idrs]*logHRs))*s1xS
        A12dS=WdevRS[,idrs]%*%(exp(dat$A[idrs]*logHRs)*dat$A[idrs])
        A13cS=(WfS[idrs]%*%exp(dat$A[idrs]*logHRs))*s1xS
        A13dS=WfS[idrs]%*%(exp(dat$A[idrs]*logHRs)*dat$A[idrs])
      }
      
      
      A12b=dat$wt[x]*(A12d/s0x-A12c/(s0x^2))
      A12x[,x]=-(A12a-A12b)
      
      A12bS=dat$swt[x]*(A12dS/s0xS-A12cS/(s0xS^2))
      A12xS[,x]=-(A12aS-A12bS)
      A13bS=dat$swt[x]*(A13dS/s0xS-A13cS/(s0xS^2))
      A13xS[x]=-(A13aS-A13bS)
    }
    
    
    A11A12=c(sum(A11x),apply(A12x,1,sum))
    A11A12A13s=c(sum(A11xS),apply(A12xS,1,sum),sum(A13xS))
    
    
    A22mat=apply(covX,1,tcrossprod)%*%(psfit*(1-psfit))
    
    A22=matrix(apply(A22mat,1,sum),nX,nX)
    
    AA=as.matrix(rbind(A11A12,cbind(0,A22)))
    invAA=solve(AA)
    
    AAs=as.matrix(rbind(A11A12A13s,cbind(0,A22,0),c(rep(0,nX+1),n)))
    invAAs=solve(AAs)
    
    
    rs11=rep(0,n)
    rs12=rs11
    
    rs11s=rep(0,n)
    rs12s=rs11s
    
    for (i in 1:n){
      ind2=which(risksetFull$time<=dat$time[i])
      rsred=risksetFull[ind2,]
      rs11[i]=dat$wt[i]*dat$A[i]*exp(log(HRest)*dat$A[i])*sum(rsred$sumWi/(rsred$sumW1*HRest+rsred$sumW0))
      rs12[i]=dat$wt[i]*exp(log(HRest)*dat$A[i])*sum(rsred$sumWi*(rsred$sumW1*HRest)/((rsred$sumW1*HRest+rsred$sumW0)^2))
      
      ind2=which(risksetFulls$time<=dat$time[i])
      rsred=risksetFulls[ind2,]
      rs11s[i]=dat$swt[i]*dat$A[i]*exp(log(HRests)*dat$A[i])*sum(rsred$sumWi/(rsred$sumW1*HRests+rsred$sumW0))
      rs12s[i]=dat$swt[i]*exp(log(HRests)*dat$A[i])*sum(rsred$sumWi*(rsred$sumW1*HRests)/((rsred$sumW1*HRests+rsred$sumW0)^2))
    }
    
    
    eta=gg-rs11+rs12
    etaS=ggS-rs11s+rs12s
    
    
    matpi=as.matrix(covX * rep(A-psfit, nX))
    
    bbmat=cbind(eta,matpi)
    
    bbmatS=cbind(etaS,matpi,A-prevA)
    
    oot=apply(bbmat,1,tcrossprod)
    BB=matrix(apply(oot,1,sum),nX+1,nX+1)
    
    propVar=invAA%*%BB%*%t(invAA)
    proposeStdErr=sqrt(propVar[1,1])
    
    ootS=apply(bbmatS,1,tcrossprod)
    BBs=matrix(apply(ootS,1,sum),nX+2,nX+2)
    
    propVarS=invAAs%*%BBs%*%t(invAAs)
    proposeStdErrS=sqrt(propVarS[1,1])
    
    lowProp=logHR-qnorm(1-(1-confidence)/2)*proposeStdErr
    upProp=logHR+qnorm(1-(1-confidence)/2)*proposeStdErr
    
    lowPropS=logHRs-qnorm(1-(1-confidence)/2)*proposeStdErrS
    upPropS=logHRs+qnorm(1-(1-confidence)/2)*proposeStdErrS
    
    mtd=c("conventional ATE weights","stabilized ATE weights")
    est=c(logHR,logHRs)
    hrest=exp(est)
    se=c(proposeStdErr,proposeStdErrS)
    low=exp(c(lowProp,lowPropS))
    up=exp(c(upProp,upPropS))
    
    zvalue <- est/se
    pValue <- 2 * pnorm(-abs(zvalue))
  
    output=cbind(est,se,hrest,low,up,pValue)
    
    colnames(output)=c("log HR Estimate","Standard Error","HR Estimate",
                       paste("HR ", confidence*100,"% CI", "-low", sep =""),
                       paste("HR ", confidence*100,"% CI", "-up", sep =""),
                       "p-value")
    rownames(output)=mtd
  } else if (weightType=="ATT"){
    
    dat$wt=A+psfit*(1-A)/(1-psfit)
    
    fit <- coxph(Surv(time, status) ~ A+cluster(id), weights=dat$wt,data=dat, ties=ties)
    logHR=fit$coefficients
    
    HRest=exp(logHR)
    
    dgvec=(1-A)*psfit/(1-psfit)
    WdevR=t(as.matrix(covX * rep(dgvec, nX))) 
    
    eventid=which(status==1)
    eventimes=unique(time[eventid])
    
    RScol5=eventimes
    RScol3=rep(0,length(eventimes))
    RScol4=RScol3
    RScol1=RScol3
    RScol2=RScol3
    
    for (i in 1:length(eventimes)){
      idc=which(dat$time==eventimes[i]&dat$status==1)
      RD1=dat[idc,]
      
      RScol1[i]=sum(RD1$wt*RD1$A)
      RScol2[i]=sum(RD1$wt)
      
      RSind=which(dat$time>=eventimes[i])
      RS1=dat[RSind,]
      
      RScol3[i]=sum(RS1$wt*RS1$A)
      RScol4[i]=sum(RS1$wt*(1-RS1$A))
      
    }
    
    risksetFull=data.frame(sumAiWi=RScol1,sumWi=RScol2,sumW1=RScol3,sumW0=RScol4,time=RScol5)
    
    gg=rep(0,n)
    
    A11x=rep(0,n)
    A12x=matrix(0,nX,n)
    
    
    for (x in eventid){
      ind1=which(risksetFull$time==dat$time[x])
      vv=(risksetFull$sumW1*HRest)/(risksetFull$sumW1*HRest+risksetFull$sumW0)
      gg[x]=dat$wt[x]*(dat$A[x]- vv[ind1])
      
      idrs=which(dat$time>=dat$time[x])
      s0x=sum(dat$wt[idrs]*exp(A[idrs]*logHR))
      s1x=sum(dat$wt[idrs]*exp(A[idrs]*logHR)*A[idrs])
      A11x[x]=dat$wt[x]*(s1x/s0x-s1x^2/(s0x^2))
      A12a=(A[x]-s1x/s0x)*WdevR[,x]
      
      if (length(idrs)==1){
        A12c=(WdevR[,idrs]*exp(A[idrs]*logHR))*s1x
        A12d=WdevR[,idrs]*(exp(A[idrs]*logHR)*A[idrs])
        
      } else{
        A12c=(WdevR[,idrs]%*%exp(dat$A[idrs]*logHR))*s1x
        A12d=WdevR[,idrs]%*%(exp(dat$A[idrs]*logHR)*dat$A[idrs])
        
      }
      
      A12b=dat$wt[x]*(A12d/s0x-A12c/(s0x^2))
      A12x[,x]=-(A12a-A12b)
      
    }
    
    
    A11A12=c(sum(A11x),apply(A12x,1,sum))
    
    A22mat=apply(covX,1,tcrossprod)%*%(psfit*(1-psfit))
    
    A22=matrix(apply(A22mat,1,sum),nX,nX)
    
    AA=as.matrix(rbind(A11A12,cbind(0,A22)))
    invAA=solve(AA)
    
    rs11=rep(0,n)
    rs12=rs11
    
    for (i in 1:n){
      ind2=which(risksetFull$time<=dat$time[i])
      rsred=risksetFull[ind2,]
      rs11[i]=dat$wt[i]*dat$A[i]*exp(log(HRest)*dat$A[i])*sum(rsred$sumWi/(rsred$sumW1*HRest+rsred$sumW0))
      rs12[i]=dat$wt[i]*exp(log(HRest)*dat$A[i])*sum(rsred$sumWi*(rsred$sumW1*HRest)/((rsred$sumW1*HRest+rsred$sumW0)^2))
      
    }
    
    eta=gg-rs11+rs12
    
    matpi=as.matrix(covX * rep(A-psfit, nX))
    
    bbmat=cbind(eta,matpi)
    
    oot=apply(bbmat,1,tcrossprod)
    BB=matrix(apply(oot,1,sum),nX+1,nX+1)
    
    propVar=invAA%*%BB%*%t(invAA)
    proposeStdErr=sqrt(propVar[1,1])
    
    lowProp=logHR-qnorm(1-(1-confidence)/2)*proposeStdErr
    upProp=logHR+qnorm(1-(1-confidence)/2)*proposeStdErr
    
    mtd=c("ATT weights")
    est=logHR
    hrest=exp(est)
    se=proposeStdErr
    low=exp(lowProp)
    up=exp(upProp)
    
    zvalue <- est/se
    pValue <- 2 * pnorm(-abs(zvalue))
    
    output=cbind(est,se,hrest,low,up,pValue)
    
    colnames(output)=c("log HR Estimate","Standard Error","HR Estimate",
                       paste("HR ", confidence*100,"% CI", "-low", sep =""),
                       paste("HR ", confidence*100,"% CI", "-up", sep =""),
                       "p-value")
    rownames(output)=mtd
  } 
  
  output
}