import { createClient } from "https://esm.sh/@supabase/supabase-js@2";

const corsHeaders = {
  'Access-Control-Allow-Origin': '*',
  'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type',
};

async function md5Lower(s: string) {
  const data = new TextEncoder().encode(s.toLowerCase());
  // Mailchimp requires MD5 of lowercase email as the subscriber hash
  const hash = await crypto.subtle.digest('MD5', data).catch(() => null);
  if (hash) {
    return Array.from(new Uint8Array(hash)).map(b => b.toString(16).padStart(2, '0')).join('');
  }
  // Fallback: use a JS MD5 implementation
  return jsMd5(s.toLowerCase());
}

// Minimal JS MD5 fallback (Deno's WebCrypto may not support MD5 in some runtimes)
function jsMd5(str: string): string {
  function rotateLeft(x: number, n: number) { return (x << n) | (x >>> (32 - n)); }
  function addUnsigned(x: number, y: number) {
    const x4 = (x & 0x40000000); const y4 = (y & 0x40000000);
    const x8 = (x & 0x80000000); const y8 = (y & 0x80000000);
    const result = (x & 0x3FFFFFFF) + (y & 0x3FFFFFFF);
    if (x4 & y4) return (result ^ 0x80000000 ^ x8 ^ y8);
    if (x4 | y4) {
      if (result & 0x40000000) return (result ^ 0xC0000000 ^ x8 ^ y8);
      else return (result ^ 0x40000000 ^ x8 ^ y8);
    } else return (result ^ x8 ^ y8);
  }
  function f(x:number,y:number,z:number){return (x&y)|((~x)&z);} 
  function g(x:number,y:number,z:number){return (x&z)|(y&(~z));} 
  function h(x:number,y:number,z:number){return x^y^z;} 
  function i(x:number,y:number,z:number){return y^(x|(~z));} 
  function ff(a:number,b:number,c:number,d:number,x:number,s:number,ac:number){a=addUnsigned(a,addUnsigned(addUnsigned(f(b,c,d),x),ac));return addUnsigned(rotateLeft(a,s),b);} 
  function gg(a:number,b:number,c:number,d:number,x:number,s:number,ac:number){a=addUnsigned(a,addUnsigned(addUnsigned(g(b,c,d),x),ac));return addUnsigned(rotateLeft(a,s),b);} 
  function hh(a:number,b:number,c:number,d:number,x:number,s:number,ac:number){a=addUnsigned(a,addUnsigned(addUnsigned(h(b,c,d),x),ac));return addUnsigned(rotateLeft(a,s),b);} 
  function ii(a:number,b:number,c:number,d:number,x:number,s:number,ac:number){a=addUnsigned(a,addUnsigned(addUnsigned(i(b,c,d),x),ac));return addUnsigned(rotateLeft(a,s),b);} 
  function convertToWordArray(str:string){
    let lWordCount; const lMessageLength=str.length;
    const lNumberOfWordsTempOne=lMessageLength+8;
    const lNumberOfWordsTempTwo=(lNumberOfWordsTempOne-(lNumberOfWordsTempOne%64))/64;
    const lNumberOfWords=(lNumberOfWordsTempTwo+1)*16;
    const lWordArray=new Array(lNumberOfWords-1).fill(0);
    let lBytePosition=0; let lByteCount=0;
    while(lByteCount<lMessageLength){
      lWordCount=(lByteCount-(lByteCount%4))/4;
      lBytePosition=(lByteCount%4)*8;
      lWordArray[lWordCount]=(lWordArray[lWordCount]|(str.charCodeAt(lByteCount)<<lBytePosition));
      lByteCount++;
    }
    lWordCount=(lByteCount-(lByteCount%4))/4;
    lBytePosition=(lByteCount%4)*8;
    lWordArray[lWordCount]=lWordArray[lWordCount]|(0x80<<lBytePosition);
    lWordArray[lNumberOfWords-2]=lMessageLength<<3;
    lWordArray[lNumberOfWords-1]=lMessageLength>>>29;
    return lWordArray;
  }
  function wordToHex(lValue:number){let wordToHexValue="",wordToHexValueTemp="",lByte,lCount;for(lCount=0;lCount<=3;lCount++){lByte=(lValue>>>(lCount*8))&255;wordToHexValueTemp="0"+lByte.toString(16);wordToHexValue=wordToHexValue+wordToHexValueTemp.substr(wordToHexValueTemp.length-2,2);}return wordToHexValue;}
  function utf8Encode(str:string){return unescape(encodeURIComponent(str));}
  const x=convertToWordArray(utf8Encode(str));
  let a=0x67452301,b=0xEFCDAB89,c=0x98BADCFE,d=0x10325476;
  const S11=7,S12=12,S13=17,S14=22,S21=5,S22=9,S23=14,S24=20,S31=4,S32=11,S33=16,S34=23,S41=6,S42=10,S43=15,S44=21;
  for(let k=0;k<x.length;k+=16){
    const AA=a,BB=b,CC=c,DD=d;
    a=ff(a,b,c,d,x[k+0],S11,0xD76AA478); d=ff(d,a,b,c,x[k+1],S12,0xE8C7B756); c=ff(c,d,a,b,x[k+2],S13,0x242070DB); b=ff(b,c,d,a,x[k+3],S14,0xC1BDCEEE);
    a=ff(a,b,c,d,x[k+4],S11,0xF57C0FAF); d=ff(d,a,b,c,x[k+5],S12,0x4787C62A); c=ff(c,d,a,b,x[k+6],S13,0xA8304613); b=ff(b,c,d,a,x[k+7],S14,0xFD469501);
    a=ff(a,b,c,d,x[k+8],S11,0x698098D8); d=ff(d,a,b,c,x[k+9],S12,0x8B44F7AF); c=ff(c,d,a,b,x[k+10],S13,0xFFFF5BB1); b=ff(b,c,d,a,x[k+11],S14,0x895CD7BE);
    a=ff(a,b,c,d,x[k+12],S11,0x6B901122); d=ff(d,a,b,c,x[k+13],S12,0xFD987193); c=ff(c,d,a,b,x[k+14],S13,0xA679438E); b=ff(b,c,d,a,x[k+15],S14,0x49B40821);
    a=gg(a,b,c,d,x[k+1],S21,0xF61E2562); d=gg(d,a,b,c,x[k+6],S22,0xC040B340); c=gg(c,d,a,b,x[k+11],S23,0x265E5A51); b=gg(b,c,d,a,x[k+0],S24,0xE9B6C7AA);
    a=gg(a,b,c,d,x[k+5],S21,0xD62F105D); d=gg(d,a,b,c,x[k+10],S22,0x2441453); c=gg(c,d,a,b,x[k+15],S23,0xD8A1E681); b=gg(b,c,d,a,x[k+4],S24,0xE7D3FBC8);
    a=gg(a,b,c,d,x[k+9],S21,0x21E1CDE6); d=gg(d,a,b,c,x[k+14],S22,0xC33707D6); c=gg(c,d,a,b,x[k+3],S23,0xF4D50D87); b=gg(b,c,d,a,x[k+8],S24,0x455A14ED);
    a=gg(a,b,c,d,x[k+13],S21,0xA9E3E905); d=gg(d,a,b,c,x[k+2],S22,0xFCEFA3F8); c=gg(c,d,a,b,x[k+7],S23,0x676F02D9); b=gg(b,c,d,a,x[k+12],S24,0x8D2A4C8A);
    a=hh(a,b,c,d,x[k+5],S31,0xFFFA3942); d=hh(d,a,b,c,x[k+8],S32,0x8771F681); c=hh(c,d,a,b,x[k+11],S33,0x6D9D6122); b=hh(b,c,d,a,x[k+14],S34,0xFDE5380C);
    a=hh(a,b,c,d,x[k+1],S31,0xA4BEEA44); d=hh(d,a,b,c,x[k+4],S32,0x4BDECFA9); c=hh(c,d,a,b,x[k+7],S33,0xF6BB4B60); b=hh(b,c,d,a,x[k+10],S34,0xBEBFBC70);
    a=hh(a,b,c,d,x[k+13],S31,0x289B7EC6); d=hh(d,a,b,c,x[k+0],S32,0xEAA127FA); c=hh(c,d,a,b,x[k+3],S33,0xD4EF3085); b=hh(b,c,d,a,x[k+6],S34,0x4881D05);
    a=hh(a,b,c,d,x[k+9],S31,0xD9D4D039); d=hh(d,a,b,c,x[k+12],S32,0xE6DB99E5); c=hh(c,d,a,b,x[k+15],S33,0x1FA27CF8); b=hh(b,c,d,a,x[k+2],S34,0xC4AC5665);
    a=ii(a,b,c,d,x[k+0],S41,0xF4292244); d=ii(d,a,b,c,x[k+7],S42,0x432AFF97); c=ii(c,d,a,b,x[k+14],S43,0xAB9423A7); b=ii(b,c,d,a,x[k+5],S44,0xFC93A039);
    a=ii(a,b,c,d,x[k+12],S41,0x655B59C3); d=ii(d,a,b,c,x[k+3],S42,0x8F0CCC92); c=ii(c,d,a,b,x[k+10],S43,0xFFEFF47D); b=ii(b,c,d,a,x[k+1],S44,0x85845DD1);
    a=ii(a,b,c,d,x[k+8],S41,0x6FA87E4F); d=ii(d,a,b,c,x[k+15],S42,0xFE2CE6E0); c=ii(c,d,a,b,x[k+6],S43,0xA3014314); b=ii(b,c,d,a,x[k+13],S44,0x4E0811A1);
    a=ii(a,b,c,d,x[k+4],S41,0xF7537E82); d=ii(d,a,b,c,x[k+11],S42,0xBD3AF235); c=ii(c,d,a,b,x[k+2],S43,0x2AD7D2BB); b=ii(b,c,d,a,x[k+9],S44,0xEB86D391);
    a=addUnsigned(a,AA); b=addUnsigned(b,BB); c=addUnsigned(c,CC); d=addUnsigned(d,DD);
  }
  return (wordToHex(a)+wordToHex(b)+wordToHex(c)+wordToHex(d)).toLowerCase();
}

Deno.serve(async (req) => {
  if (req.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  try {
    const authHeader = req.headers.get('Authorization');
    if (!authHeader) {
      return new Response(JSON.stringify({ error: 'Unauthorized' }), {
        status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      });
    }

    const supabase = createClient(
      Deno.env.get('SUPABASE_URL')!,
      Deno.env.get('SUPABASE_ANON_KEY')!,
      { global: { headers: { Authorization: authHeader } } }
    );
    const admin = createClient(
      Deno.env.get('SUPABASE_URL')!,
      Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
    );

    const { data: { user } } = await supabase.auth.getUser();
    if (!user) {
      return new Response(JSON.stringify({ error: 'Unauthorized' }), {
        status: 401, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      });
    }

    const { association_id } = await req.json();
    if (!association_id) {
      return new Response(JSON.stringify({ error: 'Missing association_id' }), {
        status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      });
    }

    // Load config
    const { data: config, error: cfgErr } = await admin
      .from('mailchimp_configs')
      .select('*')
      .eq('association_id', association_id)
      .maybeSingle();

    if (cfgErr || !config) {
      return new Response(JSON.stringify({ error: 'Mailchimp not configured for this association' }), {
        status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      });
    }
    if (!config.audience_id) {
      return new Response(JSON.stringify({ error: 'No audience selected' }), {
        status: 400, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      });
    }

    // Load owners
    const { data: owners, error: ownErr } = await admin
      .from('owners')
      .select('first_name, last_name, email, property_address, electronic_consent')
      .eq('association_id', association_id)
      .eq('status', 'active')
      .not('email', 'is', null);

    if (ownErr) {
      return new Response(JSON.stringify({ error: ownErr.message }), {
        status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      });
    }

    const baseUrl = `https://${config.server_prefix}.api.mailchimp.com/3.0`;
    const authMc = `Basic ${btoa(`anystring:${config.api_key}`)}`;

    // Use Mailchimp batch operations endpoint for efficient PUT (upsert) of members
    const operations: any[] = [];
    const validOwners = (owners || []).filter(o => o.email && o.email.includes('@'));

    for (const o of validOwners) {
      const hash = await md5Lower(o.email);
      operations.push({
        method: 'PUT',
        path: `/lists/${config.audience_id}/members/${hash}`,
        body: JSON.stringify({
          email_address: o.email,
          status_if_new: 'subscribed',
          merge_fields: {
            FNAME: o.first_name || '',
            LNAME: o.last_name || '',
            ADDRESS: o.property_address || '',
          },
        }),
      });
    }

    let succeeded = 0;
    let failed = 0;
    let lastError: string | null = null;

    if (operations.length === 0) {
      await admin.from('mailchimp_configs').update({
        last_sync_at: new Date().toISOString(),
        last_sync_status: 'success',
        last_sync_count: 0,
        last_sync_error: null,
      }).eq('id', config.id);

      return new Response(JSON.stringify({ success: true, total: 0, succeeded: 0, failed: 0 }), {
        headers: { ...corsHeaders, 'Content-Type': 'application/json' },
      });
    }

    // Send in chunks of 100 sequentially via individual PUT calls (simpler than batch endpoint)
    for (let i = 0; i < operations.length; i++) {
      const op = operations[i];
      try {
        const r = await fetch(`${baseUrl}${op.path}`, {
          method: op.method,
          headers: { Authorization: authMc, 'Content-Type': 'application/json' },
          body: op.body,
        });
        if (r.ok) {
          succeeded++;
        } else {
          failed++;
          const txt = await r.text();
          lastError = txt.slice(0, 300);
        }
      } catch (e) {
        failed++;
        lastError = (e as Error).message;
      }
    }

    const status = failed === 0 ? 'success' : (succeeded === 0 ? 'failed' : 'partial');
    await admin.from('mailchimp_configs').update({
      last_sync_at: new Date().toISOString(),
      last_sync_status: status,
      last_sync_count: succeeded,
      last_sync_error: lastError,
    }).eq('id', config.id);

    return new Response(JSON.stringify({
      success: true,
      total: operations.length,
      succeeded,
      failed,
      error: lastError,
    }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } });
  } catch (err) {
    return new Response(JSON.stringify({ error: (err as Error).message }), {
      status: 500, headers: { ...corsHeaders, 'Content-Type': 'application/json' },
    });
  }
});
