To have nicely configurable enemies, I started out with a java enum that has loads of parameters for each enemy type (and I intend to move those params to a txt-file later). This enum included the firing rate, the bullet speed, the velocity, health and so on for each enemy.
AI on the other side started very prototypic: I just had a controlling AI class for enemy ships that looked for the closest enemy planet as a move target, while checking for the player ship on its way there to fire at it if in range.
While this behaviour was static/prototypic or even hacky (I like to play around with ideas and include , e.g., a few switch case statements) I recently transformed it into something more elegant.
Any ship AI (allied as enemy) now includes a priority list for its movement target and one for its firing target selection. Enemy fighters, e.g., move towards the next enemy planet and their prio list for firing is: (enemy planet, enemy player ship).
So while the fighter moves towards the enemy planet, it will, in each framewise update of the AI class, walk over that prio list. If there is a corresponding target in range, it will break and fire at it. This implies that a fighter that is on its way to your planet (but not yet there) will fire at your ship if you come close enough. However, once it has reached your home planet, it won't try to attack your ship, but instead fire at the planet.
That simple priority pattern allows a nicely readable and maintainable implementation of enemy behaviour. A fighter that only wants to attack your planet and ignores your ship on its way their sucks. A prio list deals with that. Also you can play around with the priorization:
A stronger fighter unit (with homing missiles, e.g.) could priorize you over the planet, because - unlike the fighters - it can really do damage to your ship. The move target prio list allows for characteristic behaviour, too: A hunter unit can just priorize your ship over your planets, i.e. it would seek you out. But once your ship is destroyed and takes time to respawn it would (without any additional code) use your next planet as a new destination and fire at it until you respawn.
Now all I need to do is add an enum for AI behaviours and configure them with prio lists (simple arrays) as parameters and add a behaviour to each enemy - which I can even replace or modify on the fly.
When coding AI, keep the code small (which makes it maintainable and debuggable) and unless you are very experienced, absolutely do all sorts of debug visualization right from the start!