import com.onresolve.scriptrunner.runner.rest.common.CustomEndpointDelegate
import com.atlassian.applinks.api.ApplicationLinkResponseHandler
import com.atlassian.applinks.api.ApplicationLinkService
import com.atlassian.applinks.api.application.jira.JiraApplicationType
import com.atlassian.sal.api.component.ComponentLocator
import com.atlassian.sal.api.net.Request
import com.atlassian.sal.api.net.Response as SalResponse
import com.atlassian.sal.api.net.ResponseException
import com.atlassian.confluence.user.AuthenticatedUserThreadLocal
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
import groovy.transform.BaseScript
import javax.ws.rs.core.MultivaluedMap
import javax.ws.rs.core.Response
import java.net.URLEncoder

@BaseScript CustomEndpointDelegate delegate

/* MEDIQ V46.1 - unified Confluence DC proxy; preserves original endpoint paths and contracts.
 * INSTALL: Replace the THREE existing Confluence scripts defining:
 * mediqGateway*, mediqMaintenanceProxy*, mediqUserFaultProxy* with THIS ONE script.
 * Never install alongside them: duplicate route registration is ambiguous.
 * Existing Jira endpoints remain in Jira. Authenticated AppLink must impersonate user.
 */
def respondMediq = { int status, Object obj ->
    Response.status(status).type('application/json; charset=UTF-8').entity(JsonOutput.toJson(obj)).build()
}
def invokeMediq = { String path, String method, String body, int maximum, boolean normalizeId, String operation ->
    try {
        // Resolve the actual Confluence request principal, not SAL remoteUserKey.
        // Do not trust an identity sent by the browser or fall back to anonymous access.
        def currentUser = AuthenticatedUserThreadLocal.get()
        if (currentUser == null || !currentUser.name) {
            return respondMediq(401,[ok:false,message:'نشست کاربری معتبر شناسایی نشد. لطفاً وارد حساب خود شوید و صفحه را دوباره باز کنید.'])
        }
        if (method == 'POST') {
            if ((body ?: '').length() > maximum) return respondMediq(413,[ok:false,message:'حجم درخواست بیش از حد مجاز است.'])
            def parsed = new JsonSlurper().parseText(body ?: '{}')
            if (!(parsed instanceof Map)) return respondMediq(400,[ok:false,message:'ساختار درخواست نامعتبر است.'])
        }
        def appLinks = ComponentLocator.getComponent(ApplicationLinkService)
        def link = appLinks?.getPrimaryApplicationLink(JiraApplicationType)
        if (!link) return respondMediq(503,[ok:false,message:'اتصال سرویس مدیکیو برقرار نشده است.'])
        def factory = link.createAuthenticatedRequestFactory()
        def req = factory.createRequest(method == 'GET' ? Request.MethodType.GET : Request.MethodType.POST,path)
        req.addHeader('Accept','application/json, text/plain')
        req.setSoTimeout(90000)
        if (method == 'POST') {
            req.addHeader('Content-Type','application/json; charset=UTF-8')
            req.addHeader('X-Atlassian-Token','no-check')
            req.setRequestBody(body ?: '{}')
        }
        Map remote = req.execute(new ApplicationLinkResponseHandler<Map>() {
            @Override Map credentialsRequired(SalResponse response) throws ResponseException {
                [status:401,body:'',authRequired:true]
            }
            @Override Map handle(SalResponse response) throws ResponseException {
                [status:response.statusCode,body:response.responseBodyAsString ?: '',authRequired:false]
            }
        })
        int status = (remote.status ?: 502) as int
        if (remote.authRequired) return respondMediq(401,[ok:false,message:'اتصال حساب کاربری به سرویس مدیکیو نیازمند تأیید دسترسی است.'])
        String raw=(remote.body ?: '').toString()
        if (normalizeId && status >= 200 && status < 300 && raw.trim() ==~ /^[0-9]+$/)
            return respondMediq(status,[ok:true,id:raw.trim(),operation:operation])
        Object parsed
        try { parsed=new JsonSlurper().parseText(raw) }
        catch(Exception ignored) { return respondMediq(status >= 200 && status < 300 ? 502 : status,
            [ok:false,message:'پاسخ سرویس نامعتبر است.',httpStatus:status]) }
        return respondMediq(status,parsed)
    } catch(Exception error) {
        log.error('MEDIQ UNIFIED PROXY '+method+' '+path.take(140),error)
        return respondMediq(502,[ok:false,message:'ارتباط با سرویس مدیکیو برقرار نشد.'])
    }
}
def apiPrefix = '/rest/scriptrunner/latest/custom/'
def kindAllowed = ['groups','titles','models','equipment','metadata','manufacturer','facility',
                   'supplier','department','ownerDepartment','responsible','recipient',
                   'accessories','ownerCompany','location'] as Set

def readGatewayMediq = { MultivaluedMap qp ->
    String kind = (qp.getFirst('kind') ?: 'groups').toString()
    if (!(kind in kindAllowed) && !(kind ==~ /^equipmentRef_[0-9]{1,9}$/))
        return respondMediq(400,[ok:false,message:'نوع داده نامعتبر است.'])
    String limit=(qp.getFirst('limit') ?: '500').toString()
    String offset=(qp.getFirst('offset') ?: '0').toString()
    if (!(limit ==~ /^[0-9]{1,4}$/) || !(offset ==~ /^[0-9]{1,8}$/))
        return respondMediq(400,[ok:false,message:'صفحه‌بندی نامعتبر است.'])
    int lim=Math.min(1000,Math.max(1,limit.toInteger()))
    invokeMediq(apiPrefix+'mediqCatalog?kind='+URLEncoder.encode(kind,'UTF-8')+
      '&limit='+lim+'&offset='+offset,'GET',null,0,false,'')
}
def writeGatewayMediq = { MultivaluedMap qp, String body ->
    Map routes=[reference:'mediqCreateModelReference',model:'mediqCreateModel',
                equipment:'mediqCreateEquipment',lifecycle:'mediqOperations']
    String operation=(qp.getFirst('operation') ?: '').toString()
    if (!routes.containsKey(operation)) return respondMediq(400,[ok:false,message:'عملیات نامعتبر است.'])
    invokeMediq(apiPrefix+routes[operation],'POST',body,3000000,true,operation)
}

/* KEEP the permission boundary: catalog and write paths are for MEDIQ operators only. */
mediqGateway(httpMethod:'GET',groups:['mediq-managers','mediq-experts']) { MultivaluedMap qp,String ignored ->
    readGatewayMediq(qp)
}
mediqGatewayWrite(httpMethod:'POST',groups:['mediq-managers','mediq-experts']) { MultivaluedMap qp,String body ->
    writeGatewayMediq(qp,body)
}

/* Maintenance users retain server-side project permission checks. */
mediqMaintenanceProxy(httpMethod:'GET') { MultivaluedMap qp,String ignored ->
    String action=(qp.getFirst('action') ?: 'overview').toString()
    String type=(qp.getFirst('type') ?: '').toString()
    if (!(action ==~ /^[a-z][a-z0-9.]{0,39}$/) || type.length()>100)
        return respondMediq(400,[ok:false,message:'پارامتر نامعتبر است.'])
    String path=apiPrefix+'mediqMaintenance?action='+URLEncoder.encode(action,'UTF-8')
    if(type) path+='&type='+URLEncoder.encode(type,'UTF-8')
    invokeMediq(path,'GET',null,0,false,'')
}
mediqMaintenanceProxyWrite(httpMethod:'POST') { MultivaluedMap qp,String body ->
    invokeMediq(apiPrefix+'mediqMaintenanceWrite','POST',body,20000,false,'')
}

/* Self-service: on Jira backend, access is restricted to the authenticated reporter. */
mediqUserFaultProxy(httpMethod:'GET') { MultivaluedMap qp,String ignored ->
    String action=(qp.getFirst('action') ?: 'list').toString()
    String key=(qp.getFirst('key') ?: '').toString()
    if (!(action in ['list','detail']) || (key && !(key ==~ /^MED-[0-9]{1,12}$/)))
        return respondMediq(400,[ok:false,message:'پارامتر نامعتبر است.'])
    String path=apiPrefix+'mediqUserFault?action='+URLEncoder.encode(action,'UTF-8')
    if(key)path+='&key='+URLEncoder.encode(key,'UTF-8')
    invokeMediq(path,'GET',null,0,false,'')
}
mediqUserFaultProxyWrite(httpMethod:'POST') { MultivaluedMap qp,String body ->
    invokeMediq(apiPrefix+'mediqUserFaultWrite','POST',body,12000,false,'')
}