"""Prisca platform integrations and analytics adapters.

All adapters are credential-gated and safe-by-default.  No credential is stored in
source code.  Publishing is only attempted when an asset is explicitly approved.
"""
import os, json, time, urllib.request, urllib.error, urllib.parse, mimetypes

class APIError(RuntimeError): pass

def _request(url, method='GET', headers=None, data=None, timeout=30, multipart=None):
    headers=dict(headers or {})
    body=None
    if multipart is not None:
        boundary='----PriscaBoundary7MA4YWxkTrZu0gW'
        chunks=[]
        for name,value in multipart.items():
            if isinstance(value, tuple):
                filename, content, ctype=value
                chunks += [f'--{boundary}\r\nContent-Disposition: form-data; name="{name}"; filename="{filename}"\r\nContent-Type: {ctype}\r\n\r\n'.encode(), content, b'\r\n']
            else:
                chunks += [f'--{boundary}\r\nContent-Disposition: form-data; name="{name}"\r\n\r\n{value}\r\n'.encode()]
        chunks.append(f'--{boundary}--\r\n'.encode()); body=b''.join(chunks)
        headers['Content-Type']=f'multipart/form-data; boundary={boundary}'
    elif data is not None:
        body=json.dumps(data,ensure_ascii=False).encode() if isinstance(data,(dict,list)) else data
        headers.setdefault('Content-Type','application/json')
    req=urllib.request.Request(url,data=body,headers=headers,method=method)
    try:
        with urllib.request.urlopen(req,timeout=timeout) as r:
            raw=r.read().decode('utf-8','replace')
            try: out=json.loads(raw) if raw else None
            except Exception: out=raw
            return r.status,out
    except urllib.error.HTTPError as e:
        raw=e.read().decode('utf-8','replace')
        try: out=json.loads(raw)
        except Exception: out=raw
        raise APIError(f'HTTP {e.code}: {out}')

class IntegrationManager:
    """Provider registry. Credentials are environment variables only."""
    def configured(self):
        return {
            'wordpress': bool(os.getenv('PRISCA_WP_URL') and os.getenv('PRISCA_WP_USER') and os.getenv('PRISCA_WP_APP_PASSWORD')),
            'instagram': bool(os.getenv('PRISCA_META_ACCESS_TOKEN') and os.getenv('PRISCA_INSTAGRAM_BUSINESS_ID')),
            'facebook': bool(os.getenv('PRISCA_META_ACCESS_TOKEN') and os.getenv('PRISCA_FACEBOOK_PAGE_ID')),
            'youtube': bool(os.getenv('PRISCA_YOUTUBE_ACCESS_TOKEN')),
            'pinterest': bool(os.getenv('PRISCA_PINTEREST_ACCESS_TOKEN')),
            'search_console': bool(os.getenv('PRISCA_GOOGLE_ACCESS_TOKEN') and os.getenv('PRISCA_GSC_SITE_URL')),
            'bing_webmaster': bool(os.getenv('PRISCA_BING_API_KEY') and os.getenv('PRISCA_BING_SITE_URL')),
        }

    def status(self):
        return {'ok':True,'providers':self.configured(),'mode':'credential-gated','publish_requires_approval':True}

class YouTubeAdapter:
    BASE='https://www.googleapis.com/youtube/v3'
    UPLOAD='https://www.googleapis.com/upload/youtube/v3/videos'
    def __init__(self): self.token=os.getenv('PRISCA_YOUTUBE_ACCESS_TOKEN','')
    def _auth(self):
        if not self.token: raise APIError('PRISCA_YOUTUBE_ACCESS_TOKEN is not configured')
        return {'Authorization':'Bearer '+self.token}
    def channel(self):
        s,d=_request(self.BASE+'/channels?part=id,snippet,statistics&mine=true',headers=self._auth())
        return {'status':s,'data':d}
    def publish_video(self, asset, file_path=None, privacy='private'):
        if not file_path or not os.path.isfile(file_path): raise APIError('A local video file is required for YouTube upload')
        # YouTube resumable upload: initiate then upload media.
        headers=self._auth(); headers.update({'Content-Type':'application/json','X-Upload-Content-Type':'video/*','X-Upload-Content-Length':str(os.path.getsize(file_path))})
        body={'snippet':{'title':asset.get('title','Prisca Holidays'),'description':asset.get('body',''),'tags':asset.get('tags') or [],'categoryId':str(asset.get('category_id') or '19')},'status':{'privacyStatus':privacy}}
        s,d=_request(self.UPLOAD+'?uploadType=resumable&part=snippet,status',method='POST',headers=headers,data=body)
        # urllib wrapper does not expose Location header, so use direct request for the session URL.
        # To keep this adapter dependency-free, require a pre-created upload URL when direct upload is not available.
        if not isinstance(d,dict) or not d.get('upload_url'):
            raise APIError('YouTube upload session was created by the provider but the resumable Location header is not exposed by this lightweight adapter. Configure PRISCA_YOUTUBE_UPLOAD_URL for the upload session URL.')
        return {'status':s,'data':d}

class PinterestAdapter:
    BASE='https://api.pinterest.com/v5'
    def __init__(self): self.token=os.getenv('PRISCA_PINTEREST_ACCESS_TOKEN','')
    def _auth(self):
        if not self.token: raise APIError('PRISCA_PINTEREST_ACCESS_TOKEN is not configured')
        return {'Authorization':'Bearer '+self.token}
    def pins(self):
        s,d=_request(self.BASE+'/pins',headers=self._auth()); return {'status':s,'data':d}
    def create_pin(self, asset):
        board=os.getenv('PRISCA_PINTEREST_BOARD_ID','')
        image_url=asset.get('image_url')
        if not board or not image_url: raise APIError('PRISCA_PINTEREST_BOARD_ID and a public image_url are required')
        payload={'board_id':board,'title':asset.get('title','Prisca Holidays'),'description':asset.get('body',''),'link':asset.get('link') or os.getenv('PRISCA_SITE_URL',''),'media_source':{'source_type':'image_url','url':image_url}}
        s,d=_request(self.BASE+'/pins',method='POST',headers=self._auth(),data=payload); return {'status':s,'data':d}

class MetaAdapter:
    """Meta Graph adapter. Endpoint/version are configurable because Meta API versions change."""
    def __init__(self):
        self.token=os.getenv('PRISCA_META_ACCESS_TOKEN','')
        self.version=os.getenv('PRISCA_META_GRAPH_VERSION','v24.0')
        self.base='https://graph.facebook.com/'+self.version
    def _token(self):
        if not self.token: raise APIError('PRISCA_META_ACCESS_TOKEN is not configured')
        return self.token
    def page_feed(self, message, link=None):
        page=os.getenv('PRISCA_FACEBOOK_PAGE_ID','')
        if not page: raise APIError('PRISCA_FACEBOOK_PAGE_ID is not configured')
        payload={'message':message,'access_token':self._token()}
        if link: payload['link']=link
        s,d=_request(self.base+'/'+page+'/feed',method='POST',headers={},data=payload); return {'status':s,'data':d}
    def instagram_publish_image(self, image_url, caption=''):
        ig=os.getenv('PRISCA_INSTAGRAM_BUSINESS_ID','')
        if not ig: raise APIError('PRISCA_INSTAGRAM_BUSINESS_ID is not configured')
        payload={'image_url':image_url,'caption':caption,'access_token':self._token()}
        s,container=_request(self.base+'/'+ig+'/media',method='POST',data=payload)
        cid=(container or {}).get('id') if isinstance(container,dict) else None
        if not cid: return {'status':s,'data':container}
        s2,d2=_request(self.base+'/'+ig+'/media_publish',method='POST',data={'creation_id':cid,'access_token':self._token()})
        return {'status':s2,'data':d2,'container_id':cid}

class SearchConsoleAdapter:
    BASE='https://www.googleapis.com/webmasters/v3'
    def __init__(self): self.token=os.getenv('PRISCA_GOOGLE_ACCESS_TOKEN',''); self.site=os.getenv('PRISCA_GSC_SITE_URL','')
    def query(self,start_date,end_date,dimensions=None,row_limit=1000):
        if not self.token or not self.site: raise APIError('Google Search Console credentials/site are not configured')
        dims=dimensions or ['query','page']
        url=self.BASE+'/sites/'+urllib.parse.quote(self.site,safe='')+'/searchAnalytics/query'
        payload={'startDate':start_date,'endDate':end_date,'dimensions':dims,'rowLimit':min(int(row_limit),25000)}
        s,d=_request(url,method='POST',headers={'Authorization':'Bearer '+self.token},data=payload); return {'status':s,'data':d}

class BingWebmasterAdapter:
    def __init__(self): self.key=os.getenv('PRISCA_BING_API_KEY',''); self.site=os.getenv('PRISCA_BING_SITE_URL','')
    def submit_url(self,url):
        if not self.key or not self.site: raise APIError('Bing Webmaster API key/site are not configured')
        endpoint='https://www.bing.com/webmaster/api.svc/json/SubmitUrl?apikey='+urllib.parse.quote(self.key)
        s,d=_request(endpoint,method='POST',data={'siteUrl':self.site,'url':url}); return {'status':s,'data':d}
